Postgres row_number 返回任意排序顺序

wil*_*hen 0 sql postgresql

我跑

SELECT * 
FROM 
    (SELECT 
         *, 
         ROW_NUMBER() OVER () AS n 
     FROM 
         {table_name}) t 
WHERE 
    n < 10000 
Run Code Online (Sandbox Code Playgroud)

在 Postgres 中。我注意到每次运行的结果都不同。

为了测试除了顺序之外内容是否不同,我对列进行了平均。结果很有趣:有主键的表的返回值是一致的,而另一个没有主键的表在每次运行中都不同。

具有PK的表的执行计划:

"Aggregate  (cost=139391585.22..139391585.23 rows=1 width=32)"
"  ->  WindowAgg  (cost=0.58..99288350.02 rows=3208258816 width=9090)"
"        Run Condition: (row_number() OVER (?) < 10000)"
"        ->  Index Only Scan using mea_vit_pi_4221ef4deeadcabf_ix on {table_name}  (cost=0.58..59185114.82 rows=3208258816 width=8)"
Run Code Online (Sandbox Code Playgroud)

没有pk的表的执行计划:

"Aggregate  (cost=83580303.64..83580303.65 rows=1 width=32)"
"  ->  WindowAgg  (cost=0.00..61837074.84 rows=1739458304 width=650)"
"        Run Condition: (row_number() OVER (?) < 10000)"
"        ->  Seq Scan on {table_2}  (cost=0.00..40093846.04 rows=1739458304 width=8)"
Run Code Online (Sandbox Code Playgroud)

为什么不一样?如果可以的话,我应该怎样做才能得到稳定的结果?

Erw*_*ter 8

ROW_NUMBER() OVER ()(不带)以任意排序顺序ORDER BY返回连续的数字,无论 Postgres 碰巧以什么顺序返回行。在您的情况下,不仅同一行可以获得不同的数字,而且每次都可以选择不同的行。

对于小表,顺序通常按照物理排序顺序进行排序,但这是不可靠的!对于更大的表或更复杂的查询,会出现许多扭曲。并行性、缓存效果……

在您的特定测试中,Index Only Scan(大概是在您的 PK 指数上) vs.Seq Scan会产生差异。仅索引扫描返回排序的行。顺序扫描可以自由地以任意顺序获取最便宜的行。

添加确定性ORDER BY子句以获得确定性数字。这意味着,所有ORDER BY表达式一起必须形成每个选定行的唯一值。

在此过程中,WHERE用更便宜的条款替换该条款LIMIT

SELECT *, row_number() OVER () AS n
FROM   tbl
ORDER  BY  -- your deterministic sort order here!
LIMIT  9999;
Run Code Online (Sandbox Code Playgroud)

row_number()ORDER BY遵循相同的中设置的顺序SELECT。但要绝对明确的是:

SELECT *, row_number() OVER (ORDER  BY ... ) AS n  -- your deterministic sort order here!
FROM   tbl
ORDER  BY  ...  -- same here!
LIMIT  9999;
Run Code Online (Sandbox Code Playgroud)

两个查询都会产生相同的查询计划 - 正如当前实施的那样。只有第二个是有保证的。