我有两个CTE,第一个运行'select top n'来获取记录样本,然后第二个分割每个记录,每个记录产生2行。
我使用newid()来复制假数据,以确定样本的排序顺序。
declare @testtab table (Id bigint identity (1,1), ColA varchar(10), ColB varchar(10))
-- generate 1000 sample records
declare @cnt int = 0
while (@cnt<1000)
begin
insert @testtab(ColA, ColB) values ('A'+convert(varchar,@cnt), 'B'+convert(varchar,@cnt))
set @cnt+=1
end
;with SampleRecs as(
select top 1 * from @testtab order by newid()
)
,SplitRecs as (
select 0 Pos,Id,ColA Col from SampleRecs
union all
select 1, id, ColB col from SampleRecs
)
select * from SplitRecs
order by id, pos
预期的最终结果是每行输入行2行,如下所示:
Pos Id Col
0 720 A719
1 720 A719
然而,我得到的是像
Pos Id Col
0 720 A719
1 774 B773
因此,CTE中的“前1名”似乎在CTE2中运行了两次。
这是正常的吗?任何人都可以指出任何可以解释这种行为的文档吗?
答案 0 :(得分:3)
这是因为您通过CTE
在第二个CTE
中引用了第一个UNION ALL
两次。
CTE
本质上是临时视图,在被调用后会被丢弃。因此,对第一个CTE
的每次调用都会分别执行SELECT
语句,并且由于您的ORDER BY
已超过NEWID()
,因此您可以获得不同的TOP 1
两次执行的结果。
使用实际语句替换查询中的CTE
引用有助于说明发生的情况:
SampleRecs
定义为:
通过newid()
从@testtab顺序中选择前1 *
因此,用该子查询替换所有引用,将为您提供以下内容:
;With SplitRecs As
(
Select 0 As Pos,
Id,
ColA As Col
From (Select Top 1 * From @testtab Order By NewId()) As A
Union All
Select 1 As Pos,
Id,
ColB As Col
From (Select Top 1 * From @testtab Order By NewId()) As B
)
Select *
From SplitRecs
Order By Id, pos
您将从此获得与CTE
变体相同的行为。
要获得您期望的结果,您可以执行以下操作,相反,它利用CROSS JOIN
为Pos
值设定种子:
Select X.Pos As Pos,
Id,
ColA As Col
From (Select Top 1 * From @testtab Order By NewId()) As A
Cross Join (Select 0 As Pos Union All Select 1) As X
Order By X.Pos