【发布时间】:2021-03-13 21:22:42
【问题描述】:
SQLite 3.35.0 包括对materialized CTEs 的支持,
如果使用 MATERIALIZED 短语,则可能会评估 select-stmt 以生成保存在内存或临时磁盘文件中的临时表,当 CTE 表名出现在后续的 SQL。因为 select-stmt 是立即评估的,所以失去了应用优化(例如查询展平或下推优化)的机会。 (然后,CTE 充当“优化围栏”。)
我想构造一个例子来理解新的行为,并想出了以下查询
WITH foo AS MATERIALIZED (
SELECT RANDOM() FROM (SELECT 1)
) SELECT * FROM foo a, foo b;
使用MATERIALIZED 提示,CTE 应该存储在一个临时表中,但结果包含两个不同的数字,这表明 foo 被计算了两次。
-1208936393171353997|288817028780076083
这是查询计划。令人惊讶的是,玩(NOT) MATERIALIZED 似乎没有任何效果。
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Init 0 1 0 0
1 Integer 8 1 0 0
2 Once 0 8 0 0
3 OpenEphemeral 2 1 0 0
4 Integer 1 2 0 0
5 MakeRecord 2 1 3 0
6 NewRowid 2 4 0 0
7 Insert 2 3 4 8
8 Return 1 0 0 0
9 Integer 16 5 0 0
10 Once 0 16 0 0
11 OpenEphemeral 3 1 0 0
12 Integer 1 6 0 0
13 MakeRecord 6 1 7 0
14 NewRowid 3 8 0 0
15 Insert 3 7 8 8
16 Return 5 0 0 0
17 Rewind 2 24 0 0
18 Rewind 3 24 0 0
19 Function 0 0 9 random(0) 0
20 Function 0 0 10 random(0) 0
21 ResultRow 9 2 0 0
22 Next 3 19 0 1
23 Next 2 18 0 1
24 Halt 0 0 0 0
为什么 CTE 没有实现?作为奖励,您能否在 SQLite 3.35.0 和以前的版本下构建具有不同行为的查询(例如,通过具体化将随机数生成器转换为 getRandomNumber,就像我在这里尝试实现的那样)?
【问题讨论】:
-
应用程序不应该依赖于这个提示的语义效果来决定何时或多久调用用户定义的函数。
-
sqlite 对像
random()这样的非确定性函数所做的事情有时会非常反直觉。查看该查询生成的字节码(参见EXPLAIN)会很有用。 -
@Shawn 我知道你来自哪里,但是 SQLite 非常重视向后兼容性(他们甚至保持与以前版本中的错误的遗留兼容性),所以我很想知道是否有变化实施可能会破坏任何东西。我使用
RANDOM构建了查询,目的是利用其古怪的行为。查询计划将在稍后包含:) -
是的,它没有在临时表中保存随机值(只是 rowids)——可能是因为它们是不确定的。存储它们确实会破坏与忽略具体化提示的 CTE 行为的兼容性 - 有关
random()如何与 CTE 交互的示例,请参见 this question。 -
@nalzok 。 .我的猜测是,这是一个未记录的功能,可以在 CTE 非常简单时避免实现。
标签: sql sqlite optimization common-table-expression