Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

cte recursivo com funções de classificação

EDITAR


Ao ler a documentação do CTE sobre recursão, você notará que ela tem alguns limites, como não poder usar subconsultas, group-by, top. Todos eles envolvem várias linhas. De testes limitados e verificando o plano de execução, além de testar essa consulta
with cte as (
  select 1 a, 1 b union all select 1, 2 union all select 1, 3 union all select 2, 4
)
, rcte (a, b, c, d) as (
  select a, b, cast(0 as int), 1 
  from cte
  union all
  select r.a, cte.b, cast(ROW_NUMBER() over (order by r.b) as int), r.d+1
  from rcte r inner join cte on cte.a=r.a
  where r.d < 2
)
select * 
from rcte
where d=2
order by a, b

Só posso concluir:
  1. Row_Number() funciona em um CTE, quando outras tabelas são unidas para produzir um conjunto de resultados de várias linhas
  2. A partir dos resultados da numeração, fica claro que os CTEs são processados ​​em uma única linha por todas as iterações, linha por linha em vez de multilinha por multilinha, mesmo que pareça iterar todas as linhas simultaneamente. Isso explicaria por que nenhuma das funções que se aplicam a operações de várias linhas não são permitidas para CTE recursiva.

Embora eu tenha chegado a essa conclusão facilmente, alguém obviamente levou muito mais tempo para explicar em detalhes excruciantes somente 17 meses atrás...

Em outras palavras, essa é a natureza da implementação do SQL Server do CTE recursiva, portanto, as funções de janela não funcionarão da maneira que você espera.


Para o benefício de outros, a saída é:
a           b           c           d
----------- ----------- ----------- -----------
1           1           1           2
1           2           1           2
2           3           1           2
2           4           1           2

Considerando que você espera que c contenha 1,2,1,2 em vez de 1,1,1,1. Isso certamente parece ser um bug, já que não há documentação que diga que as funções de janela não devem funcionar na parte recursiva de um CTE.

Observação:row_number() retorna bigint, então você pode converter apenas a âncora(c) como bigint.

Como cada iteração aumenta d, você pode executar a janela externa.
with cte as (
  select 1 a, 1 b union all select 1, 2 union all select 2, 3 union all select 2, 4
)
, rcte (a, b, d) as (
  select a, b, 1 
  from cte
  union all
  select a, b, d+1
  from rcte
  where d < 2
)
select a,b, ROW_NUMBER() over (partition by a,d order by b) c,d
from rcte
--where d=2
order by d, a, b


EDITAR - insight


Ao responder outra pergunta , joguei um pouco mais com CTE recursiva. Se você executá-lo sem o ORDER BY final, poderá ver como o SQL Server está se aproximando da recursão. É interessante que ele retroceda neste caso e, em seguida, faça uma recursão de profundidade total em cada linha.

Tabela de amostra
create table Testdata(SomeID int, OtherID int, Data varchar(max))
insert Testdata select 1, 9, '18,20,22,alpha,beta,gamma,delta'
insert Testdata select 2, 6, ''
insert Testdata select 3, 8, '11,12,.'
insert Testdata select 4, 7, '13,19,20,66,12,232,1232,12312,1312,abc,def'
insert Testdata select 5, 8, '17,19'

Uma consulta recursiva
;with tmp(SomeID, OtherID, DataItem, Data) as (
select SomeID, OtherID, LEFT(Data, CHARINDEX(',',Data+',')-1),
    STUFF(Data, 1, CHARINDEX(',',Data+','), '')
from Testdata
union all
select SomeID, OtherID, LEFT(Data, CHARINDEX(',',Data+',')-1),
    STUFF(Data, 1, CHARINDEX(',',Data+','), '')
from tmp
where Data > ''
)
select SomeID, OtherID, DataItem, Data
from tmp
-- order by SomeID

A saída mostra a âncora CTE processada na iteração um, então por qualquer motivo cada linha no conjunto âncora é recursiva até a conclusão (primeiro em profundidade) antes de processar outras linhas.

No entanto, tem seus usos estranhos, como esta resposta programas