IN = bad, EXISTS = good!

Você pode estar se perguntando: IN e EXISTS são diferentes, e usados em diferentes situações! Como posso dizer que um é “bom” e o outro é “ruim”?

Assim como nos posts anteriores desta série – Tuning de SQL – temo que prestar atenção nos casos em que essas “dicas” são aplicáveis. Obviamente o EXISTS não pode simplesmente substituir o IN em todos os casos – e o desenvolvedor/programador/analista/DBA é quem deve analisar o caso e verificar se é possível aplicar esta melhoria.

Usamos o IN para verificar se um valor está contido em uma lista, enquanto o EXISTS é usado para verificar a existência de linhas retornadas por uma subconsulta. IN verifica os valores reais, enquanto EXISTS apenas verifica a existência. Geralmente EXISTS tem um melhor desempenho do que IN em subconsultas – e como nosso objetivo aqui é desempenho, devemos usar EXISTS ao invés de IN sempre que possível.

Ruim (usando IN):

SQL> SELECT d.department_id, d.department_name
  2  FROM departments d
  3  WHERE d.department_id IN
  4  (SELECT department_id FROM employees);

DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
           10 Administration
           20 Marketing
           30 Purchasing
           40 Human Resources
           50 Shipping
           60 IT
           70 Public Relations
           80 Sales
           90 Executive
          100 Finance
          110 Accounting

11 linhas selecionadas.

Bom (usando EXISTS):

SQL> SELECT department_id, department_name
  2  FROM departments d
  3  WHERE EXISTS
  4  (SELECT 1 FROM employees e
  5  WHERE e.department_id = d.department_id);

DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
           10 Administration
           20 Marketing
           30 Purchasing
           40 Human Resources
           50 Shipping
           60 IT
           70 Public Relations
           80 Sales
           90 Executive
          100 Finance
          110 Accounting

11 linhas selecionadas.

DISTINCT = bad, EXISTS = good!

Quando queremos suprimir linhas duplicadas, ou seja, não queremos linhas completamente repetidas no retorno de um SELECT, usamos a cláusula DISTINCT, correto? Para essa operação ser realizada, internamente o banco faz uma ordenação das linhas recuperadas para facilitar assim a exclusão das linhas repetidas.

Em alguns casos, é possível substituir o DISTINCT pelo EXISTS. O resultado do SELECT será o mesmo, porém, o Oracle não tem o custo de classificar todas as linhas para excluir as repetidas.

Ruim (DISTINCT):

SQL> SELECT DISTINCT d.department_id, d.department_name
  2  FROM departments d, employees e
  3  WHERE d.department_id = e.department_id;

DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
          100 Finance
           50 Shipping
           70 Public Relations
           30 Purchasing
           90 Executive
           10 Administration
          110 Accounting
           40 Human Resources
           20 Marketing
           60 IT
           80 Sales

11 linhas selecionadas.

Bom (EXISTS):

SQL> SELECT department_id, department_name
  2  FROM departments d
  3  WHERE EXISTS
  4  (SELECT 1 FROM employees e
  5  WHERE e.department_id = d.department_id);

DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
           10 Administration
           20 Marketing
           30 Purchasing
           40 Human Resources
           50 Shipping
           60 IT
           70 Public Relations
           80 Sales
           90 Executive
          100 Finance
          110 Accounting

11 linhas selecionadas.