2

再帰cteでランキング関数を使用するには? これが私がやろうとしている方法を示す簡単な例です:

cte を (
  選択 1 a, 1 b ユニオンオール セレクト 1, 2 ユニオンオール セレクト 2, 3 ユニオンオール セレクト 2, 4
)
, rcte (a, b, c, d) as (
  a、b、cast(int として 0)、1 を選択
  cteから
  ユニオンオール
  select a, b, cast(ROW_NUMBER() over (a order by b) as int), d+1
  rcteから
  ここで、d < 2
)
選択する *
rcteから
ここで、d=2
a, b 順

なんでランキングないの?私の間違いを見せてください

4

1 に答える 1

12

編集

再帰に関するCTEのドキュメントを読むと、サブクエリ、group-by、topを使用できないなど、いくつかの制限があることに気付くでしょう。これらはすべて複数の行を含みます。限定的なテスト、実行プランのチェック、およびこのクエリのテストから

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

私は結論することができるだけです:

  1. Row_Number()は、他のテーブルが結合されて複数行の結果セットを生成する場合、CTEで機能します
  2. 番号付けの結果から、CTEは、すべての行を同時に反復しているように見えますが、複数行ごとではなく、行ごとに、すべての反復を通じて単一の行で処理されることが明らかです。これは、複数行操作に適用される関数のいずれかが再帰CTEに許可されない理由を説明します。

私は簡単にこの結論に達しましたが、たった17か月前に、誰かがそれを非常に詳細に説明する のにもっと時間がかかったのは明らかです...

つまり、これはSQL Serverの再帰CTEの実装の性質であるため、ウィンドウ関数は期待どおりに機能しません。


他の人の利益のために、出力は次のとおりです。

a           b           c           d
----------- ----------- ----------- -----------
1           1           1           2
1           2           1           2
2           3           1           2
2           4           1           2

一方、cには1,1,1,1ではなく1,2,1,2が含まれていると予想しています。 ウィンドウ関数がCTEの再帰部分で機能してはならないという文書がないため、これは確かにバグのようです。

注:row_number()はbigintを返すため、anchor(c)だけをbigintとしてキャストできます。

各反復でdが増加するため、外部でウィンドウ処理を実行できます。

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


編集-洞察

別の質問リンクに回答している間、再帰CTEでもう少し遊んだ。最後のORDERBYなしで実行すると、SQLServerがどのように再帰に近づいているかを確認できます。この場合、逆方向に進み、各行で深さ優先探索を完全に実行するのは興味深いことです。

サンプルテーブル

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'

再帰クエリ

;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

出力には、反復1で処理されたCTEアンカーが表示されます。その後、何らかの理由で、アンカーセットの各行が、他の行を処理する前に(深さ優先)完了まで繰り返されます。

しかし、この答えが示すように、それは奇妙な用途を持っています

于 2011-03-24T09:47:16.053 に答える