PostgreSQL volatile 表达式和子查询

Posted

技术标签:

【中文标题】PostgreSQL volatile 表达式和子查询【英文标题】:PostgreSQL volatile expressions and subqueries 【发布时间】:2012-07-23 14:39:12 【问题描述】:

在 Bruce Momjian 的博文Generating Random Data Via SQL 中,他使用以下代码生成了 5 个随机字符串:

SELECT
(
        SELECT string_agg(x, '')
        FROM (
                SELECT chr(ascii('a') + floor(random() * 26)::integer)
                FROM generate_series(1, 40 + b * 0) as f(g)
        ) AS y(x)
) AS result
FROM generate_series(1,5) as a(b);

              result                  
------------------------------------------
 plwfwcgajxdygfissmxqsywcwiqptytjjppgrvgb
 sjaypirhuoynnvqjdgywfsfphuvzqbbilbhakyhf
 ngtabkjfqibwahlicgisijatliuwgbcuiwujgeox
 mqtnyewalettounachwjjzdrvxbbbpzogscexyfi
 dzcstpsvwpefohwkfxmhnlwteyybxejbdltwamsx
(5 rows)

我想知道为什么第 6 行的 'b * 0' 是必需的。当我删除它时,结果变为 5 个完全相同的字符串,这意味着 Postgres 缓存了外部选择表达式(结果)!

我找不到表达式缓存在 Postgres 中的工作方式。根据the documentationrandom() 函数被标记为 VOLATILE,所以,我希望任何依赖它的表达式也是 volatile。

表达式缓存在 Postgres 中是如何工作的?它在任何地方都有记录吗?为什么 'b*0' 禁用了 random() 没有的缓存?

更新:

为了研究这个问题,我将“b * 0”移动到 floor() 调用内部,使其与 random() 处于相同的位置/级别:

...
                SELECT chr(ascii('a') + floor(random() * 26 + b * 0)::integer)
                FROM generate_series(1, 40) as s(f)
...

结果仍未缓存;不同的字符串。

更新:显示问题的另一个示例

create sequence seq_test;

SELECT (SELECT nextval('seq_test')) FROM generate_series(1,5);

 ?column? 
----------
        1
        1
        1
        1
        1
(5 rows)

【问题讨论】:

【参考方案1】:

好吧,random() 本身是易变的,因此您不会得到相同字符重复到末尾的字符串。

如果您查看带有和不带有b*0 的查询计划,您会看到:

b*0:

 Function Scan on generate_series a  (cost=0.00..37530.00 rows=1000 width=4)
   SubPlan 1
     ->  Aggregate  (cost=37.51..37.52 rows=1 width=32)
           ->  Function Scan on generate_series  (cost=0.01..25.01 rows=1000 width=0)

没有b*0

 Function Scan on generate_series a  (cost=37.52..47.52 rows=1000 width=0)
   InitPlan 1 (returns $0)
     ->  Aggregate  (cost=37.50..37.51 rows=1 width=32)
           ->  Function Scan on generate_series  (cost=0.00..25.00 rows=1000 width=0)

如果 PostgreSQL 确定内部聚合不依赖于a,则将其评估为一次InitPlan,并且内部表达式的波动与否无关紧要。通过引入子查询对a的依赖,即使其成为相关子查询,必须对a的每一行重新进行评估。

【讨论】:

因此,计划器评估了每一行的内部表达式f,尽管它不依赖于它(正确地因为它是易变的),但是外部表达式被评估一次,除非它依赖于排! volatile 值不应该基于它着色任何结果吗?就像行依赖一样。 表达式的波动会阻止它被内联,但不影响规划器如何安排查询计划,这取决于子选择是否相关。 您的意思是任何子查询都被认为是稳定的,除非它依赖于外部查询的行变量!对我来说它看起来像一个错误。我希望 volatile 表达式会对任何表达式或子查询产生波动病毒效应。 InitPlan 是一种优化,它不应该改变结果。 "stable"、"volatile" 等不是子查询的属性,只有函数和表达式。 SQL 是一种描述查询的方式,而不是用于定义它的执行方式(尽管我承认这是一个弱论点,并且 SQL 以泄漏抽象而闻名) 谢谢,但我一点也不相信。 SQL 与否,因为表达式可以包含子查询,我希望波动性通过子查询传播。至少应该记录当前的行为。

以上是关于PostgreSQL volatile 表达式和子查询的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL 'NOT IN' 和子查询

正则表达式 - 查找字谜和子字谜

Doctrine Query Builder,“介于”表达式和子查询之间

JPQL 构造函数表达式中的 JPQL 和子查询

涉及变量引用和子表达式的参数模式中不带引号的标记:为啥它们有时会分成多个参数?

具有“const CompareVPtrs”类型的表达式将丢失一些 const-volatile 限定符以便调用