与临时表解决方案相比,使用 CTE 时的结果不同

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

Tl;DR

与使用临时表相比,使用 CTE 时得到不同的结果。为什么?

问题

假设我有以下数据结构:

#numbers

从1到10的简单数字表。

DROP TABLE IF EXISTS #numbers;
WITH rcte(n) AS (
  SELECT 1 AS n

   UNION ALL

  SELECT n + 1 AS n
    FROM rcte
   WHERE n < 10
)
SELECT n
  INTO #numbers
  FROM rcte;

SELECT TOP 3 n
  FROM #numbers;
n
1
2
3

#max

我现在可以使用

#numbers
创建一个包含 3 行和 3 列
id_col
max_nr
(2 到 5 之间的随机数)的表格:

DROP TABLE IF EXISTS #max;
DECLARE @min TINYINT = 2
        , @max TINYINT = 5;
SELECT CONCAT(N'id_', n) AS id_col
       , ABS(CHECKSUM(NEWID())) % (@max - @min + 1) + @min AS max_nr
  INTO #max
  FROM #numbers
 WHERE n <= 3;

SELECT *
  FROM #max; --- max_nr will of course look different each run
id_col 最大_nr
id_1 5
id_2 2
id_3 3

#result

现在我可以轻松生成我需要的表格,即一个表格,其中对于每个

id_col
我都有最多
max_nr

的每个数字
DROP TABLE IF EXISTS #result;

SELECT id_col
       , n
  FROM #max
 INNER JOIN #numbers
    ON n <= max_nr;
id_col n
id_1 1
id_1 2
id_1 3
id_1 4
id_1 5
id_2 1
id_2 2
id_3 1
id_3 2
id_3 3

CTE

如果我尝试对 A

CTE
做同样的事情,我有时会得到(取决于随机数)奇怪的结果。我的猜测是,为连接中的每一行重新计算随机部分,导致不同的结果,如果是这种情况,是使用临时表的唯一解决方案,或者有机会用
CTE
来解决这个问题?

NB. 重新运行以下代码几次,您最终会发现有“漏洞”,即缺少数字:

WITH cte_max (id_col, max_nr) AS (
  SELECT CONCAT(N'id_', n) AS id_col
         , ABS(CHECKSUM(NEWID())) % (@max - @min + 1) + @min AS max_nr
    FROM #numbers
   WHERE n <= 3
)
SELECT id_col
       , n
  FROM cte_max
 INNER JOIN #numbers
    ON n <= max_nr;

例如,我在一次运行中得到了这个结果:

id_col n 评论
id_1 1
id_1 2
id_1 4 3 失踪!!
id_1 5
id_2 1
id_2 2
id_2 3
id_3 1
id_3 2

你看到三个不见了。并不是所有的运行都会发生这种情况(有时所有数字都是有序的,但它们时不时会丢失,我清楚地看到我忽略了一些东西)

问题

有人可以向我解释一下为什么会出现这种行为吗? SQL Server 并行化其任务以及在内连接中随机数尚未具体化并为每个连接结果重新计算是否与此有关?如果是这样,除了使用临时表之外还有其他解决方法吗?

sql sql-server random common-table-expression
1个回答
0
投票

RAND()
执行一次随机操作,我们可以在
WITH
:

中依赖它的值
create table numbers(nr int);
insert into numbers(nr)
values
(1),
(2),
(3),
(4),
(5),
(6),
(7),
(8),
(9),
(10);

create table other_numbers(nr int);
insert into other_numbers(nr)
values
(1),
(2),
(3),
(4),
(5),
(6),
(7),
(8),
(9),
(10);
WITH rand_num (nr, randomized) AS (
  SELECT nr,
         CEILING(10 * RAND()) AS random
    FROM numbers
)
SELECT rand_num.nr AS nr1, other_numbers.nr AS nr2, rand_num.randomized
FROM rand_num
JOIN other_numbers
ON rand_num.nr = other_numbers.nr;

小提琴:http://sqlfiddle.com/#!18/8e80d6/6

© www.soinside.com 2019 - 2024. All rights reserved.