如何在CTE中生成随机数而不在JOIN中更改?

问题描述 投票:3回答:3

问题

我正在使用#Table_1为CTE表this technique中的每一行生成一个随机数。然后我在另一张桌子#Table_2上加入了CTE的结果。我没有为#Table_1中的每一行获取一个随机数,而是为连接中的每个结果行获取一个新的随机数!

CREATE TABLE #Table_1 (Id INT)

CREATE TABLE #Table_2 (MyId INT, ParentId INT)

INSERT INTO #Table_1
VALUES (1), (2), (3)

INSERT INTO #Table_2
VALUES (1, 1), (2, 1), (3, 1), (4, 1), (1, 2), (2, 2), (3, 2), (1, 3)


;WITH RandomCTE AS
(
    SELECT Id, (ABS(CHECKSUM(NewId())) % 5)RandomNumber
    FROM #Table_1
)
SELECT r.Id, t.MyId, r.RandomNumber
FROM RandomCTE r
INNER JOIN #Table_2 t
    ON r.Id = t.ParentId

结果

Id          MyId        RandomNumber
----------- ----------- ------------
1           1           1
1           2           2
1           3           0
1           4           3
2           1           4
2           2           0
2           3           0
3           1           3

期望的结果

Id          MyId        RandomNumber
----------- ----------- ------------
1           1           1
1           2           1
1           3           1
1           4           1
2           1           4
2           2           4
2           3           4
3           1           3

我尝试了什么

我试图通过将随机数转换为VARCHAR来模糊优化器中随机数生成的逻辑,但这不起作用。

我不想做什么

我想避免使用临时表来存储CTE的结果。

如何为表生成随机数并在连接中保留该随机数而不使用临时存储?

sql sql-server random common-table-expression
3个回答
2
投票

这似乎可以解决问题:

WITH CTE AS(
    SELECT Id, (ABS(CHECKSUM(NewId())) % 5)RandomNumber
    FROM #Table_1),
RandomCTE AS(
    SELECT Id,
           RandomNumber
    FROM CTE
    GROUP BY ID, RandomNumber)
SELECT *
FROM RandomCTE r
INNER JOIN #Table_2 t
    ON r.Id = t.ParentId;

看起来SQL Server意识到,在CTE之外,RandomNumber实际上只是NEWID(),其中包含一些额外的功能(DB<>Fiddle),因此它仍然为每一行生成一个唯一的ID。因此,第二个CTE中的GROUP BY子句强制数据引擎将RandomNumber定义为一个值,以便它可以执行GROUP BY


1
投票

根据this answer的报价

优化器不保证标量函数的执行时间或执行次数。这是一个长期建立的宗旨。它是基本的“余地”,它允许优化器有足够的自由度来获得查询计划执行方面的重大改进。

如果对您的应用程序来说重要的是,应该对随机数进行一次评估,并且只应该预先计算一次并将其存储到临时表中。

其他任何事情都不能得到保证,因此添加到您的应用程序的代码库中是不负责任的 - 即使它现在可以正常工作,它也可能因架构更改/执行计划更改/版本升级/ CU安装而中断。

例如,如果向#Table_1 (Id)添加唯一索引,拉姆的答案就会中断


0
投票

如何不使用真正的随机数?使用rand()种子:

WITH RandomCTE AS (
      SELECT Id,
             CONVERT(INT, RAND(ROW_NUMBER() OVER (ORDER BY NEWID()) * 999999) * 5) as RandomNumber
      FROM #Table_1
     )
SELECT r.Id, t.MyId, r.RandomNumber
FROM RandomCTE rINNER JOIN
     #Table_2 t
     ON r.Id = t.ParentId;

rand()的种子争论非常糟糕。彼此接近的种子的值产生相似的初始值,这是乘法的原因。

Here是db <>小提琴。

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