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 / 12941
Accepted
Curtis
Curtis
Asked: 2011-09-20 07:06:59 +0800 CST2011-09-20 07:06:59 +0800 CST 2011-09-20 07:06:59 +0800 CST

O SQL Server lê tudo de uma função COALESCE mesmo que o primeiro argumento não seja NULL?

  • 772

Estou usando uma COALESCEfunção T-SQL em que o primeiro argumento não será nulo em cerca de 95% das vezes em que for executado. Se o primeiro argumento for NULL, o segundo argumento é um processo bastante demorado:

SELECT COALESCE(c.FirstName
                ,(SELECT TOP 1 b.FirstName
                  FROM TableA a 
                  JOIN TableB b ON .....)
                )

Se, por exemplo, c.FirstName = 'John', o SQL Server ainda executaria a subconsulta?

Eu sei que com a IIF()função VB.NET, se o segundo argumento for True, o código ainda lê o terceiro argumento (mesmo que não seja usado).

sql-server null
  • 8 8 respostas
  • 19754 Views

8 respostas

  • Voted
  1. Best Answer
    JNK
    2011-09-20T07:10:23+08:002011-09-20T07:10:23+08:00

    Não . Aqui está um teste simples:

    SELECT COALESCE(1, (SELECT 1/0)) -- runs fine
    SELECT COALESCE(NULL, (SELECT 1/0)) -- throws error
    

    Se a segunda condição for avaliada, uma exceção será lançada para divisão por zero.

    De acordo com a documentação do MSDN , isso está relacionado a como COALESCEé visto pelo intérprete - é apenas uma maneira fácil de escrever uma CASEdeclaração.

    CASEé bem conhecido por ser uma das únicas funções no SQL Server que (principalmente) entram em curto-circuito de forma confiável.

    Existem algumas exceções ao comparar variáveis ​​e agregações escalares, conforme mostrado por Aaron Bertrand em outra resposta aqui (e isso se aplicaria a CASEe COALESCE):

    DECLARE @i INT = 1;
    SELECT CASE WHEN @i = 1 THEN 1 ELSE MIN(1/0) END;
    

    irá gerar uma divisão por erro zero.

    Isso deve ser considerado um bug e, como regra COALESCE, será analisado da esquerda para a direita.

    • 98
  2. Aaron Bertrand
    2011-09-20T09:47:15+08:002011-09-20T09:47:15+08:00

    Que tal este - conforme relatado a mim por Itzik Ben-Gan, que foi informado por Jaime Lafargue ?

    DECLARE @i INT = 1;
    SELECT CASE WHEN @i = 1 THEN 1 ELSE MIN(1/0) END;
    

    Resultado:

    Msg 8134, Level 16, State 1, Line 2
    Divide by zero error encountered.
    

    Existem soluções alternativas triviais, é claro, mas o ponto ainda é que CASEnem sempre garante a avaliação / curto-circuito da esquerda para a direita. Eu relatei o bug aqui e ele foi fechado como "propositalmente". Paul White subseqüentemente arquivou este item Connect e foi fechado como Fixo. Não porque foi corrigido per se, mas porque eles atualizaram os Manuais Online com uma descrição mais precisa do cenário em que as agregações podem alterar a ordem de avaliação de uma CASEexpressão. Recentemente, escrevi mais sobre isso aqui .

    EDIT apenas um adendo, embora eu concorde que esses são casos extremos, que na maioria das vezes você pode confiar na avaliação e curto-circuito da esquerda para a direita, e que esses são bugs que contradizem a documentação e provavelmente serão corrigidos eventualmente ( isso não é definitivo - veja a conversa de acompanhamento na postagem do blog de Bart Duncan para ver o porquê), tenho que discordar quando as pessoas dizem que algo é sempre verdade, mesmo que haja um único caso extremo que o refute. Se Itzik e outros podem encontrar bugs solitários como este, pelo menos existe a possibilidade de que existam outros bugs também. E como não sabemos o restante da consulta do OP, não podemos dizer com certeza que ele vai contar com esse curto-circuito, mas acabará sendo mordido por ele. Então, para mim, a resposta mais segura é:

    Embora você geralmente possa CASEavaliar a esquerda para a direita e o curto-circuito, conforme descrito na documentação, não é correto dizer que você sempre pode fazer isso. Há dois casos demonstrados nesta página em que isso não é verdade e nenhum bug foi corrigido em qualquer versão disponível publicamente do SQL Server.

    EDIT aqui é outro caso (preciso parar de fazer isso) em que uma CASEexpressão não é avaliada na ordem que você esperaria, mesmo que nenhuma agregação esteja envolvida.

    • 75
  3. Paul White
    2011-09-20T10:43:33+08:002011-09-20T10:43:33+08:00

    A documentação deixa razoavelmente claro que a intenção é causar CASEum curto-circuito. Como Aaron menciona , houve vários casos relatados em que isso nem sempre é verdade. Até agora, a maioria deles foi reconhecida como bugs e corrigida.

    Existem outros problemas com CASE(e, portanto, COALESCE) onde as funções de efeito colateral ou subconsultas são usadas. Considerar:

    SELECT COALESCE((SELECT CASE WHEN RAND() <= 0.5 THEN 999 END), 999);
    SELECT ISNULL((SELECT CASE WHEN RAND() <= 0.5 THEN 999 END), 999);
    

    O COALESCEformulário geralmente retorna nulo, conforme descrito em um relatório de bug de Hugo Kornelis.

    Os problemas demonstrados com as transformações do otimizador e o rastreamento de expressão comum significam que é impossível garantir que CASEhaverá um curto-circuito em todas as circunstâncias.

    Eu acho que você pode estar razoavelmente confiante de que CASEhaverá um curto-circuito em geral (particularmente se uma pessoa razoavelmente qualificada inspecionar o plano de execução e esse plano de execução for 'aplicado' com um guia de plano ou dicas), mas se você precisar de uma garantia absoluta, você tem que escrever SQL que não inclua a expressão.

    • 39
  4. Martin Smith
    2012-07-09T03:43:52+08:002012-07-09T03:43:52+08:00

    Eu me deparei com outro caso em CASEque COALESCEnão curto-circuito. O TVF a seguir gerará uma violação de PK se for passado 1como parâmetro.

    CREATE FUNCTION F (@P INT)
    RETURNS @T TABLE (
      C INT PRIMARY KEY)
    AS
      BEGIN
          INSERT INTO @T
          VALUES      (1),
                      (@P)
    
          RETURN
      END
    

    Se chamado da seguinte forma

    DECLARE @Number INT = 1
    
    SELECT COALESCE(@Number, (SELECT number
                              FROM   master..spt_values
                              WHERE  type = 'P'
                                     AND number = @Number), 
                             (SELECT TOP (1)  C
                              FROM   F(@Number))) 
    

    Ou como

    DECLARE @Number INT = 1
    
    SELECT CASE
             WHEN @Number = 1 THEN @Number
             ELSE (SELECT TOP (1) C
                   FROM   F(@Number))
           END 
    

    Ambos dão o resultado

    Violação da restrição PRIMARY KEY 'PK__F__3BD019A800551192'. Não é possível inserir chave duplicada no objeto 'dbo.@T'. O valor da chave duplicada é (1).

    mostrando que SELECT(ou pelo menos a população da variável da tabela) ainda é realizada e gera um erro, mesmo que esse ramo da instrução nunca deva ser alcançado. O plano para a COALESCEversão está abaixo.

    Plano

    Esta reescrita da consulta parece evitar o problema

    SELECT COALESCE(Number, (SELECT number
                              FROM   master..spt_values
                              WHERE  type = 'P'
                                     AND number = Number), 
                             (SELECT TOP (1)  C
                              FROM   F(Number))) 
    FROM (VALUES(1)) V(Number)   
    

    que dá plano

    Plano2

    • 21
  5. Martin Smith
    2013-09-25T13:05:20+08:002013-09-25T13:05:20+08:00

    Outro exemplo

    CREATE TABLE T1 (C INT PRIMARY KEY)
    
    CREATE TABLE T2 (C INT PRIMARY KEY)
    
    INSERT INTO T1 
    OUTPUT inserted.* INTO T2
    VALUES (1),(2),(3);
    

    A pergunta

    SET STATISTICS IO ON;
    
    SELECT T1.C,
           COALESCE(T1.C , CASE WHEN EXISTS (SELECT * FROM T2 WHERE T2.C = T1.C)  THEN -1 END)
    FROM T1
    OPTION (LOOP JOIN)
    

    Não mostra nenhuma leitura T2.

    A busca de T2está sob um predicado de passagem e o operador nunca é executado. Mas

    SELECT T1.C,
           COALESCE(T1.C , CASE WHEN EXISTS (SELECT * FROM T2 WHERE T2.C = T1.C)  THEN -1 END)
    FROM T1
    OPTION (MERGE JOIN)
    

    Mostra que T2é lido. Mesmo que nenhum valor from T2seja realmente necessário.

    É claro que isso não é realmente surpreendente, mas achei que valia a pena adicionar ao repositório de contra-exemplos apenas porque levanta a questão do que significa um curto-circuito em uma linguagem declarativa baseada em conjunto.

    • 10
  6. ErikE
    2012-02-09T21:24:42+08:002012-02-09T21:24:42+08:00

    Eu só queria mencionar uma estratégia que você pode não ter considerado. Pode não ser uma correspondência aqui, mas às vezes é útil. Veja se esta modificação lhe dá algum desempenho melhor:

    SELECT COALESCE(c.FirstName
                ,(SELECT TOP 1 b.FirstName
                  FROM TableA a 
                  JOIN TableB b ON .....
                  WHERE C.FirstName IS NULL) -- this is the changed part
                )
    

    Outra maneira de fazer isso pode ser esta (basicamente equivalente, mas permite acessar mais colunas da outra consulta, se necessário):

    SELECT COALESCE(c.FirstName, x.FirstName)
    FROM
       TableC c
       OUTER APPLY (
          SELECT TOP 1 b.FirstName
          FROM
             TableA a 
             JOIN TableB b ON ...
          WHERE
             c.FirstName IS NULL -- the important part
       ) x
    

    Basicamente, esta é uma técnica de junção "forte" de tabelas, mas incluindo a condição de quando qualquer linha deve ser unida. Na minha experiência, isso realmente ajudou os planos de execução às vezes.

    • 8
  7. Joe Celko
    2018-12-18T10:50:25+08:002018-12-18T10:50:25+08:00

    O padrão atual diz que todas as cláusulas WHEN (bem como a cláusula ELSE) devem ser analisadas para determinar o tipo de dados da expressão como um todo. Eu realmente teria que pegar algumas das minhas anotações antigas para determinar como um erro é tratado. Mas, de imediato, 1/0 usa números inteiros, então eu diria que, embora seja um erro. É um erro com o tipo de dados inteiro. Quando você tem apenas nulos na lista de coalescência, é um pouco mais complicado determinar o tipo de dados, e esse é outro problema.

    • 3
  8. Adriano Carneiro
    2011-09-20T07:09:02+08:002011-09-20T07:09:02+08:00

    Não, não seria. Só seria executado quando c.FirstNamefor NULL.

    No entanto, você deve tentar por si mesmo. Experimentar. Você disse que sua subconsulta é longa. Referência. Tire suas próprias conclusões sobre isso.

    A resposta @Aaron na subconsulta sendo executada é mais completa.

    No entanto, ainda acho que você deve retrabalhar sua consulta e usar LEFT JOIN. Na maioria das vezes, as subconsultas podem ser removidas retrabalhando sua consulta para usar LEFT JOINs.

    O problema com o uso de subconsultas é que sua instrução geral será executada mais lentamente porque a subconsulta é executada para cada linha no conjunto de resultados da consulta principal.

    • 2

relate perguntas

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • NULL ou NOT NULL por padrão?

  • Quando usar NULL e quando usar uma string vazia?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

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

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

    • 4 respostas
  • Marko Smith

    Como você mostra o SQL em execução em um banco de dados Oracle?

    • 2 respostas
  • Marko Smith

    Como selecionar a primeira linha de cada grupo?

    • 6 respostas
  • Marko Smith

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

    • 10 respostas
  • Marko Smith

    Posso ver Consultas Históricas executadas em um banco de dados SQL Server?

    • 6 respostas
  • Marko Smith

    Como uso currval() no PostgreSQL para obter o último id inserido?

    • 10 respostas
  • Marko Smith

    Como executar o psql no Mac OS X?

    • 11 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
  • Marko Smith

    Passando parâmetros de array para um procedimento armazenado

    • 12 respostas
  • Martin Hope
    Manuel Leduc Restrição exclusiva de várias colunas do PostgreSQL e valores NULL 2011-12-28 01:10:21 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Stuart Blackler Quando uma chave primária deve ser declarada sem cluster? 2011-11-11 13:31:59 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +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
  • Martin Hope
    BrunoLM Guid vs INT - Qual é melhor como chave primária? 2011-01-05 23:46:34 +0800 CST
  • Martin Hope
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +0800 CST
  • Martin Hope
    Patrick Como posso otimizar um mysqldump de um banco de dados grande? 2011-01-04 13:13:48 +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