典型递归时的意外数据

问题描述 投票:-1回答:1

我很难用语言来描述,所以这里是样本。

select *
into t
from (values (10, 'A'),
             (25, 'B'),
             (30, 'C'),
             (45, 'D'),
             (52, 'E'),
             (61, 'F'),
             (61, 'G'),
             (61, 'H'),
             (79, 'I'),
             (82, 'J')
) v(userid, name)

注意F,G和H有相同的userid.

现在,考虑以下递归查询。

with tn as 
(
    select t.userId,t.name, row_number() over (order by userid,newid()) as seqnum
    from t
),
cte as 
(
        select userId, name,  seqnum as seqnum
        from tn 
        where seqnum = 1
    union all
        select tn.userId, tn.name,tn.seqnum
        from 
            cte 
            inner join tn on tn.seqnum = cte.seqnum + 1
)
select *
from cte

第一个cte,tn,创建一个包含随机化组件的row_number,它将导致FGH以随机顺序出现。然而,它们仍然会各自出现一次(可以通过修改最后一个和最外层的行号来检查)。from 将要 from tn)

第二节: cte递归扫描 tn. 里面没有什么太复杂的东西,因为它来自于一个最小化的例子。然而,很明显,锚构件被手动设置成了 tn的第一行,然后递归扫描其余所有行。

然而,最终的结果集并不是每行都出现一次FGH!它们总共出现了3行,但可以任意组合。它们总共出现在3行中,但可以任意组合。FGH是可能的,但FFH也是可能的,甚至FFF也是可能的! 这是一个FHH的例子。

+--------+------+--------+
| userId | name | seqnum |
+--------+------+--------+
|     10 | A    |      1 |
|     25 | B    |      2 |
|     30 | C    |      3 |
|     45 | D    |      4 |
|     52 | E    |      5 |
|     61 | F    |      6 |
|     61 | H    |      7 |
|     61 | H    |      8 |
|     79 | I    |      9 |
|     82 | J    |     10 |
+--------+------+--------+

为什么会这样?

我想不是这样的 Ctes内的分析函数行为 有什么关系,因为 tn 其中包含的row_number不是递归的。

郑重声明,我得到这个问题是由于以下一系列的事件:有人问到 问号 的答案是 贤臣.同一个OP问 追问,我以为只要在原件上做一点手脚就能回答。但是,经过一番研究,我发现了一个我无法理解的行为。我尽可能的把例子做的最小化。

sql-server tsql recursion common-table-expression row-number
1个回答
3
投票

CTE的并不是spooled。 每次提到 tn 可能会导致重新运行查询并重新随机化结果。

为了避免这种情况,运行一次随机化查询并加载一个临时表。

select t.userId,t.name, row_number() over (order by userid,newid()) as seqnum
into #tn
from t

并在随后的查询中引用

with tn as 
(
    select * from #tn
),
cte as 
(
        select userId, name,  seqnum as seqnum
        from tn
        where seqnum = 1
    union all
        select tn.userId, tn.name,tn.seqnum
        from 
            cte 
            inner join tn on tn.seqnum = cte.seqnum + 1
)
select *
from cte
© www.soinside.com 2019 - 2024. All rights reserved.