Estou escrevendo em uma próxima postagem do meu blog sobre classificação e funções de janela agregadas, especificamente os iteradores do projeto de segmento e sequência. A forma como eu entendo é que Segment identifica linhas em um stream que constituem o fim/início de um grupo, então a seguinte consulta:
SELECT ROW_NUMBER() OVER (PARTITION BY someGroup ORDER BY someOrder)
Usará Segmento para informar quando uma linha pertence a um grupo diferente da linha anterior. O iterador Sequence Project, em seguida, faz o cálculo do número de linha real, com base na saída do iterador de segmento.
Mas a consulta a seguir, usando essa lógica, não deve incluir um segmento, porque não há expressão de partição.
SELECT ROW_NUMBER() OVER (ORDER BY someGroup, someOrder)
No entanto, quando tento essa hipótese, ambas as consultas usam um operador de segmento. A única diferença é que a segunda consulta não precisa de um GroupBy
no segmento. Isso não elimina a necessidade de um segmento em primeiro lugar?
Exemplo
CREATE TABLE dbo.someTable (
someGroup int NOT NULL,
someOrder int NOT NULL,
someValue numeric(8, 2) NOT NULL,
PRIMARY KEY CLUSTERED (someGroup, someOrder)
);
--- Query 1:
SELECT ROW_NUMBER() OVER (PARTITION BY someGroup ORDER BY someOrder)
FROM dbo.someTable;
--- Query 2:
SELECT ROW_NUMBER() OVER (ORDER BY someGroup, someOrder)
FROM dbo.someTable;
Encontrei esta postagem de blog de 6 anos mencionando o mesmo comportamento.
Parece que
ROW_NUMBER()
sempre inclui um operador de segmento, sejaPARTITION BY
usado ou não. Se eu tivesse que adivinhar, diria que é porque facilita a criação de um plano de consulta no mecanismo.Se o segmento for necessário na maioria dos casos, e nos casos em que não for necessário for essencialmente uma não operação de custo zero, é muito mais simples incluí-lo sempre no plano quando uma função de janela é usada.
De acordo com o showplan.xsd para o plano de execução,
GroupBy
aparece semminOccurs
oumaxOccurs
atributos que, portanto, padronizam para [1..1] tornando o elemento obrigatório, não necessariamente conteúdo. O elemento filhoColumnReference
do tipo (ColumnReferenceType
) temminOccurs
0 emaxOccurs
ilimitado [0..*], tornando-o opcional , portanto, o elemento vazio permitido. Se você tentar remover manualmenteGroupBy
e forçar o plano, receberá o erro esperado:Curiosamente, descobri que você pode remover manualmente o operador Segmento para obter um plano válido para forçar que se parece com isso:
No entanto, quando você executa com esse plano (usando
OPTION ( USE PLAN ... )
), o Operador de segmento reaparece magicamente. Apenas mostra que o otimizador leva apenas os planos XML como um guia aproximado.Meu equipamento de teste:
Retire o plano XML do equipamento de teste e salve-o como um .sqlplan para visualizar o plano menos o Segmento.
PS Eu não gastaria muito tempo cortando planos SQL manualmente, como se você me conhecesse, você saberia que eu considero isso um trabalho ocupado que consome tempo e algo que eu nunca faria. Ah espera!? : )