【问题标题】:Why does RANDOM() in a SQLite CTE JOIN behave differently to other RDBMSs?为什么 SQLite CTE JOIN 中的 RANDOM() 行为与其他 RDBMS 不同?
【发布时间】:2018-05-11 09:46:04
【问题描述】:

公用表表达式 (CTE) 连接中的RANDOM() 值的行为与SQLite 中的预期不同。

SQL:

WITH
  tbl1(n) AS (SELECT 1 UNION ALL SELECT 2),
  tbl2(n, r) AS (SELECT n, RANDOM() FROM tbl1)
SELECT * FROM tbl2 t1 CROSS JOIN tbl2 t2;

示例 SQLite 结果:

n   r                       n   r
1   7058971975145008000     1   8874103142384122000
1   1383551786055205600     2   8456124381892735000
2   2646187515714600000     1   7558324128446983000
2   -1529979429149869800    2   7003770339419606000

每一列的随机数都是不同的。但是CROSS JOIN 重复行 - 所以我希望每列中有 2 对相同的数字 - PostgreSQLOracle 11gSQL Server 2014 的情况(使用行时 -基于种子)。

PostgreSQL / Oracle 11g / SQL Server 2014 结果示例:

n   r                   n   r
1   0.117551110684872   1   0.117551110684872
1   0.117551110684872   2   0.221985165029764
2   0.221985165029764   1   0.117551110684872
2   0.221985165029764   2   0.221985165029764

问题

  1. 能否解释 SQLite 中的行为?是错误吗?
  2. 有没有办法让 CTE 中的表 B(基于同一 CTE 中的表 A)增加一列随机生成的数字,在 JOIN 中使用时保持固定?

【问题讨论】:

  • 我不确定你的意思是什么。 SQLite 和 SQL Server 是 2 个完全不同的 RDBMS。应该预料到两者之间的不同行为;它们的行为不会完全一样。
  • 当然可以,但为什么我们在结果中看不到相同的随机数对?
  • 根据 SQL Server documentation具有相同种子值的 RAND() 重复调用返回相同的结果。 在整个查询中返回相同的数字是为了意料之中。
  • 但是没有指定种子值,所以我希望它们都不同:如果没有指定种子,SQL Server 数据库引擎会随机分配一个种子值。
  • 在 SQL Server 中,一些非确定性函数,例如 RAND() 和 GETDATE() 每次查询只计算一次,也就是说,它们不会随着查询而改变它们的值在跑。如果你想要 SQL Server 中的“随机性”,可以使用 NEWID(),例如 SORT BY NEWID()

标签: sql sqlite random common-table-expression


【解决方案1】:

您的问题相当冗长且杂乱无章——没有一个问题。但是,这很有趣,我学到了一些东西。

此说法不正确:

SQL Server 为 RAND() 函数分配一个随机种子:当用于 一个 SELECT,它只播种一次,而不是为每一行播种。

SQL Server 具有运行时常量函数的概念。这些是从已编译查询中提取并在查询开始时执行一次每个表达式 的函数。最突出的例子是getdate()(以及相关的日期/时间函数)和rand()

如果你运行,你可以很容易地看到这个:

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

每一列的值相同,但列之间的值不同。

大多数数据库(包括 SQLite)对 rand()/random() 都有更直观的解释。 (作为个人说明,在每一行上返回相同值的“随机”函数非常违反直觉。)每次调用它时,您都会得到不同的值。对于 SQL Server,您通常会使用使用 newid() 的表达式:

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

至于您的第二个问题,似乎 SQLite 实现了递归 CTE。所以这就是你想要的:

WITH tbl1(n) AS (
      SELECT 1 UNION ALL SELECT 2
     ),
     tbl2(n, r) AS (
       SELECT n, RANDOM()
       FROM tbl1
       union all
       select *
       from tbl2
       where 1=0
      )
SELECT *
FROM tbl2 t1 CROSS JOIN tbl2 t2;

我没有看到任何文件证明是这种情况,因此使用风险自负。 Here 是一个 DB-Fiddle。

而且,作为记录,这似乎也适用于 SQL Server。我刚刚学到了一些东西!

编辑:

正如评论中所建议的,物化可能并不总是发生。它似乎确实适用于同一级别的两个参考:

WITH tbl1(n) AS (
      SELECT 1 UNION ALL SELECT 2),
     tbl2(n, r) AS (
       SELECT n, RANDOM()
       FROM tbl1
       union all
       select *
       from tbl2
       where 1=0
      )
SELECT t2a.r, count(*)
FROM tbl2 t2a left JOIN
     tbl2 t2b
     on t2a.r = t2b.r
GROUP BY t2a.r;

【讨论】:

  • 感谢您花时间研究此问题以及建议的解决方法,这似乎在所问问题的上下文中有效。但是,进一步的子选择似乎没有给出我希望的结果 - 请参阅 this DB-Fiddle:如果 CTE 真正实现,预计这里的计数均为 1。
  • @SteveChambers。 . .我在一段时间内看到的最有趣的问题之一。有趣的是,该代码确实适用于同一 FROM 中的表引用。无论如何,这是未记录的行为,我看不到强制 SQLite 实现 CTE 的方法。顺便说一句,对于其他一些数据库,物化是默认选项。
猜你喜欢
  • 2017-02-19
  • 1970-01-01
  • 2021-06-23
  • 1970-01-01
  • 2021-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多