Por que as consultas são analisadas de forma a não permitir o uso de aliases de coluna na maioria das cláusulas?

16

Ao tentar escrever uma consulta, descobri (da maneira mais difícil) que o SQL Server analisa WHEREs em uma consulta muito antes de analisar os SELECTs ao executar uma consulta.

Os documentos do MSDN dizem que a ordem de análise lógica geral é tal que SELECT é analisado quase por último (resultando em erros de "não existe tal objeto [alias]" "ao tentar usar um alias de coluna em outras cláusulas). Houve até uma sugestão para permitir que aliases fossem usados ​​em qualquer lugar, o que foi diminuído pela equipe da Microsoft, citando problemas de conformidade com os padrões ANSI (o que sugere que esse comportamento faz parte do padrão ANSI).

Como programador (não um DBA), achei esse comportamento um tanto confuso, pois me parece que ele derrota amplamente o objetivo de ter aliases de coluna (ou, pelo menos, aliases de coluna poderiam ser significativamente mais poderosos se fossem analisado anteriormente na execução da consulta), pois o único local em que você pode realmente usar os aliases é em ORDER BY. Como programador, parece que está perdendo uma grande oportunidade para tornar as consultas mais poderosas, convenientes e SECA.

Parece que é um problema tão evidente que é lógico que existem outras razões para decidir que aliases de coluna não devem ser permitidos em outra coisa senão SELECT e ORDER BY, mas quais são esses motivos?

Shauna
fonte

Respostas:

19

Sumário

Não há razão lógica para isso não ser possível, mas o benefício é pequeno e existem algumas armadilhas que podem não ser imediatamente aparentes.

Resultados da pesquisa

Eu fiz algumas pesquisas e encontrei algumas informações boas. A seguir, uma citação direta de uma fonte primária confiável (que deseja permanecer anônima) em 09/08/2012 17:49 GMT:

Quando o SQL foi inventado, ele não tinha alias na cláusula SELECT. Essa foi uma falha grave que foi corrigida quando o idioma foi padronizado pelo ANSI em 1986.

O idioma foi planejado para ser "não processual" - em outras palavras, para descrever os dados que você deseja, sem especificar como encontrá-los. Portanto, até onde eu sei, não há razão para que uma implementação SQL não possa analisar toda a consulta antes de processá-la e permitir que aliases sejam definidos em qualquer lugar e usados ​​em qualquer lugar. Por exemplo, não vejo nenhum motivo para a seguinte consulta não ser válida:

select name, salary + bonus as pay
from employee
where pay > 100000

Embora eu ache que essa é uma consulta razoável, alguns sistemas baseados em SQL podem introduzir restrições ao uso de aliases por algum motivo relacionado à implementação. Não me surpreendo ao saber que o SQL Server faz isso.

Estou interessado em mais pesquisas sobre o padrão SQL-86 e por que os DBMS modernos não suportam a reutilização de alias, mas ainda não tive tempo de ir muito longe com ele. Para começar, não sei onde obter a documentação ou como descobrir quem exatamente compôs o comitê. Alguém pode ajudar? Também gostaria de saber mais sobre o produto original da Sybase de onde o SQL Server veio.

A partir desta pesquisa e de algumas reflexões adicionais, suspeitei que o uso de aliases em outras cláusulas, embora seja possível, simplesmente nunca foi uma prioridade tão alta para os fabricantes de DBMS em comparação com outros recursos de idioma. Como não é um obstáculo muito grande, ser facilmente contornado pelo gravador de consultas, não é ideal esforçar-se por outros avanços. Além disso, seria proprietário, pois obviamente não faz parte do padrão SQL (embora eu esteja esperando para descobrir mais sobre isso com certeza) e, portanto, seria uma pequena melhoria, quebrando a compatibilidade SQL entre DBMSes. Por comparação, CROSS APPLY(que na verdade nada mais é do que uma tabela derivada que permite referências externas) é uma grande mudança, enquanto o proprietário oferece um poder expressivo incrível, que não é facilmente executado de outras maneiras.

Problemas com o uso de aliases em todos os lugares

Se você permitir que itens SELECT sejam inseridos na cláusula WHERE, poderá não apenas explodir a complexidade da consulta (e, portanto, a complexidade de encontrar um bom plano de execução), como também é possível criar coisas completamente ilógicas. Experimentar:

SELECT X + 5 Y FROM MyTable WHERE Y = X

E se MyTable já tiver uma coluna Y, qual é a cláusula WHERE? A solução é usar uma CTE ou uma tabela derivada, que na maioria dos casos não deve custar mais, mas alcança o mesmo resultado final. CTEs e tabelas derivadas, pelo menos, reforçam a resolução de ambiguidade, permitindo que um alias seja usado apenas uma vez.

Além disso, não usar aliases na cláusula FROM faz sentido eminente. Você não pode fazer isso:

SELECT
   T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
FROM
   Table1 T
   INNER JOIN Table2 T2
      ON T2.ID = CalcID
   INNER JOIN Table3 T3
      ON T2.ID = T3.ID

Isso é uma referência circular (no sentido em que T2 é secretamente referindo-se a um valor de T3, antes que a tabela foi apresentado na lista JOIN), e danado difícil de ver. Que tal este:

INSERT dbo.FinalTransaction
SELECT
   newid() FinalTransactionGUID,
   'GUID is: ' + Convert(varchar(50), FinalTransactionGUID) TextGUID,
   T.*
FROM
   dbo.MyTable T

Quanto você quer apostar que a função newid () será colocada no plano de execução duas vezes, fazendo com que as duas colunas mostrem valores diferentes completamente inesperadamente? E quando a consulta acima é usada N níveis de profundidade em CTEs ou tabelas derivadas. Garanto que o problema é pior do que você pode imaginar. Há graves problemas de inconsistência sobre quando as coisas são avaliadas somente uma vez ou em que ponto em um plano de consulta e Microsoft disse que não vai resolveralguns deles porque estão expressando álgebra de consulta corretamente - se houver resultados inesperados, divida a consulta em partes. Permitir referências encadeadas, detectar referências circulares através de cadeias potencialmente muito longas - esses são problemas bastante complicados. Introduzir paralelismo e você tem um pesadelo em formação.

Nota: O uso do alias em WHERE ou GROUP BY não fará diferença nos problemas com funções como newid () ou rand ().

Uma maneira do SQL Server de criar expressões reutilizáveis

O CROSS APPLY / OUTTER APPLY é uma maneira no SQL Server de criar expressões que podem ser usadas em qualquer outro lugar da consulta (apenas anteriormente na cláusula FROM):

SELECT
   X.CalcID
FROM
   Table1 T
   INNER JOIN Table3 T3
      ON T.ID = T3.ID
   CROSS APPLY (
      SELECT
         T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
   ) X
   INNER JOIN Table2 T2
      ON T2.ID = X.CalcID

Isso faz duas coisas:

  1. Faz com que todas as expressões no CROSS APPLY obtenham um "namespace" (um alias de tabela, aqui, X) e sejam exclusivas dentro desse namespace.
  2. Torna óbvio em todos os lugares não apenas que o CalcID é proveniente do X, mas também torna óbvio por que você não pode usar nada do X ao ingressar na tabela T1 e T3, porque o X ainda não foi introduzido.

Na verdade, eu gosto bastante de CROSS APPLY. Tornou-se meu amigo fiel, e eu o uso o tempo todo. Precisa de um UNPIVOT parcial (o que exigiria um PIVOT / UNPIVOT ou UNPIVOT / PIVOT usando sintaxe nativa)? Feito com CROSS APPLY. Precisa de um valor calculado que será reutilizado várias vezes? Feito. Precisa aplicar rigidamente a ordem de execução de chamadas em um servidor vinculado? Concluído - com uma gritante melhoria na velocidade. Precisa de apenas um tipo de linha dividida em 2 linhas ou com condições extras? Feito.

Portanto, no mínimo, no DBMS SQL Server 2005 e versões posteriores, você não tem mais motivo para reclamação: CROSS APPLY é como você SECA da maneira que deseja.

ErikE
fonte
14

Não posso lhe explicar os motivos exatos, mas vou lhe dizer que existem soluções alternativas para repetir expressões, por exemplo, usando CTEs, subconsultas, tabelas derivadas etc. para evitar repetições.

Se você mostrar uma consulta com uma expressão repetida, provavelmente podemos mostrar como reescrevê-la para que a expressão seja listada apenas uma vez. No entanto, isso apenas reduz a complexidade na escrita / leitura da consulta, é improvável que mude muito sobre a eficiência. O SQL Server geralmente é muito bom em reconhecer que as expressões são repetidas e não executará esse trabalho duas vezes. Há exceções que seguem o caminho inverso, mas você só deve se preocupar com eficiência quando observar esse fato. Suspeito que a maioria das expressões repetidas que você escreve seja realmente colapsada em apenas uma operação no plano.

Dito isso, também vou repetir parte da minha resposta desta pergunta:

/dba/19762/why-is-the-select-clause-listed-first


Aqui está a explicação de Joe Celko sobre como uma consulta é processada de acordo com o padrão (roubei isso do meu próprio artigo aspfaq.com , que roubou a citação provavelmente de uma postagem do Celko em um grupo de notícias):

Aqui está como um SELECT funciona no SQL ... pelo menos em teoria. Produtos reais otimizarão as coisas quando puderem.

Comece na cláusula FROM e construa uma tabela de trabalho a partir de todas as junções, uniões, interseções e quaisquer outros construtores de tabela existentes. A opção AS permite que você atribua um nome a esta tabela de trabalho que você precisará usar para o restante da consulta que o contém.

Vá para a cláusula WHERE e remova as linhas que não passam nos critérios; isto é, que não testam como VERDADEIRO (rejeite DESCONHECIDO e FALSO). A cláusula WHERE é aplicada ao trabalho na cláusula FROM.

Vá para a cláusula opcional GROUP BY, crie grupos e reduza cada grupo a uma única linha, substituindo a tabela de trabalho original pela nova tabela agrupada. As linhas de uma tabela agrupada devem ser características do grupo: (1) uma coluna de agrupamento (2) uma estatística sobre o grupo (ou seja, funções agregadas) (3) uma função ou (4) uma expressão composta pelos três itens.

Vá para a cláusula opcional HAVING e aplique-a na mesa de trabalho agrupada; se não houver cláusula GROUP BY, trate a tabela inteira como um grupo.

Vá para a cláusula SELECT e construa as expressões na lista. Isso significa que as subconsultas escalares, chamadas de função e expressões no SELECT são feitas após todas as outras cláusulas. O operador AS também pode atribuir um nome a expressões na lista SELECT. Esses novos nomes passam a existir todos de uma vez, mas após a cláusula WHERE ter sido executada; você não pode usá-los na lista SELECT ou no WHERE cluase por esse motivo.

As expressões de consulta aninhada seguem as regras de escopo usuais que você esperaria de uma linguagem estruturada em bloco como C, Pascal, Algol, etc. Ou seja, as consultas mais internas podem fazer referência a colunas e tabelas nas consultas em que estão contidas.

Isso significa que um SELECT não pode ter mais colunas que um GROUP BY; mas certamente pode ter menos colunas.

Agora, a Celko foi um dos principais contribuidores das versões anteriores dos padrões. Não sei se você conseguirá uma resposta definitiva para a WHY?pergunta, exceto especulações. Meu palpite é que listar a operação real primeiro torna muito fácil para o analisador saber exatamente qual será o tipo de operação. Imagine uma junção de 20 tabelas que pode acabar sendo um SELECTor UPDATEou DELETEe lembre-se de que o código para esses mecanismos foi originalmente escrito nos dias em que a análise de strings era bastante cara.

Observe que, se o padrão SQL determinou o FROMprimeiro, os fornecedores podem ter decidido analisar de maneira independente a gramática em uma ordem diferente; portanto, ainda não faz sentido esperar que a ordem das cláusulas escritas obedeça completamente à ordem de processamento de 100% de A Hora.

O mesmo vale para coisas como CASE. Vimos cenários aqui neste site , por exemplo, onde o mito anteriormente acreditado que CASEsempre processa em ordem e em curto-circuito é falso. E isso também se estende a outras crenças comuns, como o SQL Server avaliando junções na ordem em que foram gravadas, cláusulas de curto-circuito WHEREda esquerda para a direita ou processando CTEs uma vez ou em uma determinada ordem, mesmo que sejam referenciadas várias vezes. Os produtos são gratuitos para otimizar a forma como entenderem, mesmo que isso não reflita exatamente como você declarou que a consulta deve funcionar declarativamente.

Aaron Bertrand
fonte
2
Observe também que a capacidade de usar ou não aliases em diferentes partes da consulta é imposta pelo analisador, não pelo otimizador ou pelo mecanismo de execução. Como o mecanismo realmente executa a consulta não reflete necessariamente as restrições que afetam a sintaxe.
Aaron Bertrand
2

No Entity SQL , você PODE usar aliases de expressões em outros lugares da consulta em algumas situações:

select k1, count(t.a), sum(t.a)
from T as t
group by t.b + t.c as k1

Observe que aqui você DEVE definir a expressão na GROUP BYcláusula para usá-la na SELECTcláusula.

Obviamente, é possível permitir parte desse tipo de expressão de alias como reutilizável em consultas SQL.

ErikE
fonte