AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 159413
Accepted
joanolo
joanolo
Asked: 2016-12-30 00:02:32 +0800 CST2016-12-30 00:02:32 +0800 CST 2016-12-30 00:02:32 +0800 CST

EXISTS (SELECT 1 ...) vs EXISTS (SELECT * ...) Um ou outro?

  • 772

Sempre que preciso verificar a existência de alguma linha em uma tabela, costumo escrever sempre uma condição como:

SELECT a, b, c
  FROM a_table
 WHERE EXISTS
       (SELECT *  -- This is what I normally write
          FROM another_table
         WHERE another_table.b = a_table.b
       )

Algumas outras pessoas escrevem assim:

SELECT a, b, c
  FROM a_table
 WHERE EXISTS
       (SELECT 1   --- This nice '1' is what I have seen other people use
          FROM another_table
         WHERE another_table.b = a_table.b
       )

Quando a condição é NOT EXISTSem vez de EXISTS: Em algumas ocasiões, posso escrevê-la com a LEFT JOINe uma condição extra (às vezes chamada de antijoin ):

SELECT a, b, c
  FROM a_table
       LEFT JOIN another_table ON another_table.b = a_table.b
 WHERE another_table.primary_key IS NULL

Eu tento evitá-lo porque acho que o significado é menos claro, especialmente quando o que é seu primary_keynão é tão óbvio, ou quando sua chave primária ou sua condição de junção é multi-coluna (e você pode facilmente esquecer uma das colunas). No entanto, às vezes você mantém o código escrito por outra pessoa... e ele está lá.

  1. Existe alguma diferença (além do estilo) para usar SELECT 1em vez de SELECT *?
    Existe algum caso de canto em que não se comporte da mesma maneira?

  2. Embora o que escrevi seja o SQL padrão (AFAIK): Existe essa diferença para diferentes bancos de dados / versões mais antigas?

  3. Existe alguma vantagem em escrever explicitamente um antijoin?
    Os planejadores/otimizadores contemporâneos a tratam de forma diferente da NOT EXISTScláusula?

sql-server mysql
  • 7 7 respostas
  • 129527 Views

7 respostas

  • Voted
  1. Best Answer
    ypercubeᵀᴹ
    2016-12-30T00:21:11+08:002016-12-30T00:21:11+08:00

    Não, não há diferença de eficiência entre (NOT) EXISTS (SELECT 1 ...)e (NOT) EXISTS (SELECT * ...)em todos os principais SGBDs. Muitas vezes eu vi (NOT) EXISTS (SELECT NULL ...)ser usado também.

    Em alguns você pode até escrever (NOT) EXISTS (SELECT 1/0 ...)e o resultado é o mesmo - sem nenhum erro (divisão por zero), o que comprova que a expressão ali nem é avaliada.


    Sobre o LEFT JOIN / IS NULLmétodo antijoin, uma correção: isso é equivalente a NOT EXISTS (SELECT ...).

    Neste caso, NOT EXISTScontraLEFT JOIN / IS NULL, você pode obter diferentes planos de execução. No MySQL, por exemplo, e principalmente em versões mais antigas (antes de 5.7), os planos seriam bastante semelhantes, mas não idênticos. Os otimizadores de outros SGBDs (SQL Server, Oracle, Postgres, DB2) são - que eu saiba - mais ou menos capazes de reescrever esses 2 métodos e considerar os mesmos planos para ambos. Ainda assim, não há essa garantia e ao fazer a otimização, é bom verificar os planos de diferentes reescritas equivalentes, pois pode haver casos em que cada otimizador não reescreve (por exemplo, consultas complexas, com muitas junções e/ou tabelas derivadas / subconsultas dentro da subconsulta, onde as condições de várias tabelas, colunas compostas usadas nas condições de junção) ou as opções e planos do otimizador são afetados de maneira diferente pelos índices disponíveis, configurações etc.

    Observe também que USINGnão pode ser usado em todos os DBMS (SQL Server por exemplo). O mais comum JOIN ... ONfunciona em todos os lugares.
    E as colunas precisam ser prefixadas com o nome/alias da tabela SELECTpara evitar erros/ambiguidades quando temos junções.
    Eu também geralmente prefiro colocar a coluna unida na IS NULLverificação (embora o PK ou qualquer coluna não anulável esteja OK, pode ser útil para eficiência quando o plano LEFT JOINusa um índice não clusterizado):

    SELECT a_table.a, a_table.b, a_table.c
      FROM a_table
           LEFT JOIN another_table 
               ON another_table.b = a_table.b
     WHERE another_table.b IS NULL ;
    

    Há também um terceiro método para antijoins, usando NOT INmas isso tem semântica diferente (e resultados!) se a coluna da tabela interna for anulável. Ele pode ser usado excluindo as linhas com NULL, tornando a consulta equivalente às 2 versões anteriores:

    SELECT a, b, c
      FROM a_table
     WHERE a_table.b NOT IN 
           (SELECT another_table.b
              FROM another_table
             WHERE another_table.b IS NOT NULL
           ) ;
    

    Isso também geralmente produz planos semelhantes na maioria dos SGBDs.

    • 57
  2. Andriy M
    2016-12-30T05:35:14+08:002016-12-30T05:35:14+08:00

    Há uma categoria de casos em que SELECT 1e SELECT *não são intercambiáveis ​​– mais especificamente, um sempre será aceito nesses casos, enquanto o outro não será.

    Estou falando de casos em que você precisa verificar a existência de linhas de um conjunto agrupado . Se a tabela Ttiver colunas C1e C2você estiver verificando a existência de grupos de linhas que correspondem a uma condição específica, você pode usar SELECT 1assim:

    EXISTS
    (
      SELECT
        1
      FROM
        T
      GROUP BY
        C1
      HAVING
        AGG(C2) = SomeValue
    )
    

    mas você não pode usar SELECT *da mesma maneira.

    Isso é apenas um aspecto sintático. Onde ambas as opções são aceitas sintaticamente, você provavelmente não terá diferença em termos de desempenho ou resultados retornados, conforme explicado na outra resposta .

    Notas adicionais após comentários

    Parece que poucos produtos de banco de dados realmente suportam essa distinção. Produtos como SQL Server, Oracle, MySQL e SQLite aceitarão alegremente SELECT *a consulta acima sem nenhum erro, o que provavelmente significa que eles tratam um EXISTS SELECTde uma maneira especial.

    PostgreSQL é um RDBMS onde SELECT *pode falhar, mas ainda pode funcionar em alguns casos. Em particular, se você estiver agrupando pelo PK, SELECT *funcionará bem, caso contrário, falhará com a mensagem:

    ERRO: a coluna "T.C2" deve aparecer na cláusula GROUP BY ou ser usada em uma função agregada

    • 13
  3. Rick James
    2016-12-30T09:32:18+08:002016-12-30T09:32:18+08:00

    A "prova" de que eles são idênticos (no MySQL) é fazer

    EXPLAIN EXTENDED
        SELECT EXISTS ( SELECT * ... ) AS x;
    SHOW WARNINGS;
    

    depois repita com SELECT 1. Em ambos os casos, a saída 'estendida' mostra que foi transformada em SELECT 1.

    Da mesma forma, COUNT(*)é transformado em COUNT(0).

    Outra coisa a ser observada: melhorias de otimização foram feitas em versões recentes. Pode valer a pena comparar EXISTScom anti-junções. Sua versão pode fazer um trabalho melhor com um do que com o outro.

    • 5
  4. Hannah Vernon
    2016-12-30T13:25:00+08:002016-12-30T13:25:00+08:00

    Uma maneira indiscutivelmente interessante de reescrever a EXISTScláusula que resulta em uma consulta mais limpa e talvez menos enganosa, pelo menos no SQL Server, seria:

    SELECT a, b, c
      FROM a_table
     WHERE b = ANY
           (
              SELECT b
              FROM another_table
           );
    

    A versão anti-semi-join seria assim:

    SELECT a, b, c
      FROM a_table
     WHERE b <> ALL
           (
              SELECT b
              FROM another_table
           );
    

    Ambos são normalmente otimizados para o mesmo plano que WHERE EXISTSou WHERE NOT EXISTS, mas a intenção é inconfundível, e você não tem "estranho" 1ou *.

    Curiosamente, os problemas de verificação de nulos associados NOT IN (...)são problemáticos para <> ALL (...), enquanto o NOT EXISTS (...)não sofre desse problema. Considere as duas tabelas a seguir com uma coluna anulável:

    IF OBJECT_ID('tempdb..#t') IS NOT NULL
    BEGIN
        DROP TABLE #t;
    END;
    CREATE TABLE #t 
    (
        ID INT NOT NULL IDENTITY(1,1)
        , SomeValue INT NULL
    );
    
    IF OBJECT_ID('tempdb..#s') IS NOT NULL
    BEGIN
        DROP TABLE #s;
    END;
    CREATE TABLE #s 
    (
        ID INT NOT NULL IDENTITY(1,1)
        , SomeValue INT NULL
    );
    

    Adicionaremos alguns dados a ambos, com algumas linhas que correspondem e outras que não:

    INSERT INTO #t (SomeValue) VALUES (1);
    INSERT INTO #t (SomeValue) VALUES (2);
    INSERT INTO #t (SomeValue) VALUES (3);
    INSERT INTO #t (SomeValue) VALUES (NULL);
    
    SELECT *
    FROM #t;
    
    +--------+-----------+
    | ID | AlgumValor |
    +--------+-----------+
    | 1 | 1 |
    | 2 | 2 |
    | 3 | 3 |
    | 4 | NULO |
    +--------+-----------+
    INSERT INTO #s (SomeValue) VALUES (1);
    INSERT INTO #s (SomeValue) VALUES (2);
    INSERT INTO #s (SomeValue) VALUES (NULL);
    INSERT INTO #s (SomeValue) VALUES (4);
    
    SELECT *
    FROM #s;
    
    +--------+-----------+
    | ID | AlgumValor |
    +--------+-----------+
    | 1 | 1 |
    | 2 | 2 |
    | 3 | NULO |
    | 4 | 4 |
    +--------+-----------+

    A NOT IN (...)consulta:

    SELECT *
    FROM #t 
    WHERE #t.SomeValue NOT IN (
        SELECT #s.SomeValue
        FROM #s 
        );
    

    Tem o seguinte plano:

    insira a descrição da imagem aqui

    A consulta não retorna nenhuma linha, pois os valores NULL tornam a igualdade impossível de confirmar.

    Esta consulta, com <> ALL (...)mostra o mesmo plano e não retorna nenhuma linha:

    SELECT *
    FROM #t 
    WHERE #t.SomeValue <> ALL (
        SELECT #s.SomeValue
        FROM #s 
        );
    

    insira a descrição da imagem aqui

    A variante usando NOT EXISTS (...), mostra uma forma de plano ligeiramente diferente e retorna linhas:

    SELECT *
    FROM #t 
    WHERE NOT EXISTS (
        SELECT 1
        FROM #s 
        WHERE #s.SomeValue = #t.SomeValue
        );
    

    O plano:

    insira a descrição da imagem aqui

    Os resultados dessa consulta:

    +--------+-----------+
    | ID | AlgumValor |
    +--------+-----------+
    | 3 | 3 |
    | 4 | NULO |
    +--------+-----------+

    Isso torna o uso <> ALL (...)tão propenso a resultados problemáticos quanto o NOT IN (...).

    • 5
  5. Evan Carroll
    2017-03-24T18:29:46+08:002017-03-24T18:29:46+08:00

    Em alguns bancos de dados essa otimização ainda não funciona. Como por exemplo no PostgreSQL A partir da versão 9.6, isso falhará.

    SELECT *
    FROM ( VALUES (1) ) AS g(x)
    WHERE EXISTS (
      SELECT *
      FROM ( VALUES (1),(1) )
        AS t(x)
      WHERE g.x = t.x
      HAVING count(*) > 1
    );
    

    E isso vai dar certo.

    SELECT *
    FROM ( VALUES (1) ) AS g(x)
    WHERE EXISTS (
      SELECT 1  -- This changed from the first query
      FROM ( VALUES (1),(1) )
        AS t(x)
      WHERE g.x = t.x
      HAVING count(*) > 1
    );
    

    Está falhando porque o seguinte falha, mas isso ainda significa que há uma diferença.

    SELECT *
    FROM ( VALUES (1),(1) ) AS t(x)
    HAVING count(*) > 1;
    

    Você pode encontrar mais informações sobre essa peculiaridade e violação da especificação em minha resposta à pergunta, A especificação do SQL requer um GROUP BY em EXISTS ()

    • 5
  6. G DeMasters
    2016-12-30T23:52:43+08:002016-12-30T23:52:43+08:00

    Sempre usei select top 1 'x'(SQL Server)

    Teoricamente, select top 1 'x'seria mais eficiente que select *, pois o primeiro estaria completo após selecionar uma constante sobre a existência de uma linha qualificadora, enquanto o segundo selecionaria tudo.

    NO ENTANTO, embora muito cedo possa ter sido relevante, a otimização tornou a diferença irrelevante em provavelmente todos os principais RDBS.

    • -1
  7. ajeh
    2018-03-23T12:55:17+08:002018-03-23T12:55:17+08:00

    IF EXISTS(SELECT TOP(1) 1 FROMé um hábito melhor a longo prazo e entre plataformas simplesmente porque você não precisa nem começar a se preocupar com o quão boa ou ruim é sua plataforma/versão atual; e SQL está se movendo TOP npara parametrizável TOP(n). Esta deve ser uma habilidade de aprender uma vez.

    • -5

relate perguntas

  • Existem ferramentas de benchmarking do MySQL? [fechado]

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • Quando é o momento certo para usar o MariaDB em vez do MySQL e por quê?

  • Como um grupo pode rastrear alterações no esquema do banco de dados?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve