【问题标题】:Random values in iterative cte's迭代 cte 中的随机值
【发布时间】:2019-11-10 06:39:04
【问题描述】:

使用 SQL Server 2016+

我在从表中选择随机行时遇到了一些困难,该表已缩小为随机数生成方式的问题。为了提高效率,使用TOP 10 * ORDER BY NEWID() 会变慢。在下面的代码中,我使用了一组种子来获得可重复的结果,但在现场我不会。

代码示例:

SELECT  RAND(100) RN
,       RAND()
,       RAND()
,       RAND()
,       RAND();

SELECT  RAND(100) RN
UNION ALL SELECT  RAND()
UNION ALL SELECT  RAND()
UNION ALL SELECT  RAND()
UNION ALL SELECT  RAND();

WITH cte AS
    (SELECT 1         ID
     ,      RAND(100) RN
     UNION ALL
     SELECT cte.ID + 1
     ,      RAND()
       FROM cte
      WHERE ID < 5)
SELECT  RN
  FROM  cte;

结果集

RN                                                                                          
---------------------- ---------------------- ---------------------- ---------------------- ----------------------
0.715436657367485      0.28463380767982       0.0131039082850364     0.28769876521071       0.100505471175005

(1 row affected)

RN
----------------------
0.715436657367485
0.28463380767982
0.0131039082850364
0.28769876521071
0.100505471175005

(5 rows affected)

RN
----------------------
0.715436657367485
0.28463380767982
0.28463380767982
0.28463380767982
0.28463380767982

(5 rows affected)

从结果中可以看出,当我调用 RAND() 函数重复内联或通过联合时,每次调用都会得到不同的结果。但是,如果我在迭代 cte 中调用该函数,那么我会重复相同的值。

此代码是显示问题的示例,而不是整个代码集。我创建这个纯粹是为了演示这个问题。我有一个基于ChecksumNewID() 组合的解决方案,以及模数调用和乘法来给我想要的范围内的值,但这相当复杂,而且对于仅在范围。

我正在寻找任何可以提供的指导

  1. 为什么会这样
  2. 解决问题的任何方法
  3. 生成随机数列表的其他选项(非 RBAR)

非常感谢。

【问题讨论】:

  • 关于stackoverflow.com/questions/848872/…的一些其他建议
  • SQL Server 有时只对每个 SQL 语句计算一次函数 - 例如,您会看到使用 GetDate() 的相同效果,无论选择记录花费多长时间,它都不会在任何查询上增加 - 正如您所说, NewId() 没有表现出这一点,所以它适用于你想要的。我相信,其背后的原因部分是性能
  • 谢谢大家。我已经看到了这类解决方案。按 NewID() 排序在大型表上变得非常低效,我现有的解决方法是基于映射到一行的 newid 的绝对校验和。这些都是好帖子,我相信如果他们登陆这里,他们会帮助其他人。
  • >>生成随机数列表的其他选项(不是 RBAR) - 从某个地方下载数百万位数的 pi 并将其放入带有 id 的表中,然后您需要做的就是是生成一个随机 id 作为种子,那么您可以获取以下 pi 数字 - newid() 本身效率低下,还是只是排序的成本?

标签: sql sql-server random common-table-expression


【解决方案1】:

RAND() 在查询中返回一个“常量”值。也就是说,查询中的每个“提及”都会对其进行一次评估。

如果你运行,你可以看到这个:

select rand(), rand()
from (values (1), (2), (3)) v(x);

每一行都有两个不同的值。但是,这些列具有相同的值。

无论如何,规范的答案是使用RAND(CHECKSUM(NEWID()))。这会在每次调用时为 RAND() 提供一个随机种子:

WITH cte AS
    (SELECT 1 as ID, RAND(CHECKSUM(NEWID())) as RN
     UNION ALL
     SELECT cte.ID + 1, RAND(CHECKSUM(NEWID())) as RN
     FROM cte
     WHERE ID < 5
    )
SELECT RN
FROM cte;

这可能足以获得随机值列表。我怀疑你的 real 问题是不同的——比如从表中提取随机行。如果这是真正的问题,请提出问题或检查类似问题。

【讨论】:

  • 很好的答案,我很欣赏这些例子
猜你喜欢
  • 2012-03-04
  • 1970-01-01
  • 1970-01-01
  • 2013-05-27
  • 2017-10-16
  • 2017-06-10
  • 2012-11-07
  • 2012-09-30
  • 2019-03-30
相关资源
最近更新 更多