【发布时间】:2011-04-26 23:00:37
【问题描述】:
postgresql 中有什么方法可以模拟 rownum 吗?
【问题讨论】:
标签: postgresql row-number rownum
postgresql 中有什么方法可以模拟 rownum 吗?
【问题讨论】:
标签: postgresql row-number rownum
我认为可以使用临时序列来模仿 Oracle rownum。
create or replace function rownum_seq() returns text as $$
select concat('seq_rownum_',replace(uuid_generate_v4()::text,'-','_'));
$$ language sql immutable;
create or replace function rownum(r record, v_seq_name text default rownum_seq()) returns bigint as $$
declare
begin
return nextval(v_seq_name);
exception when undefined_table then
execute concat('create temporary sequence ',v_seq_name,' minvalue 1 increment by 1');
return nextval(v_seq_name);
end;
$$ language plpgsql volatile;
演示:
select ccy_code,rownum(a.*) from (select ccy_code from currency order by ccy_code desc) a where rownum(a.*)<10;
给予:
ZWD 1
ZMK 2
ZBH 3
ZAR 4
YUN 5
YER 6
XXX 7
XPT 8
XPF 9
解释:
函数rownum_seq()是不可变的,PG在一次查询中只调用一次,所以我们得到相同的唯一序列名(即使该函数在同一个查询中被调用千次)
函数 rownum() 是可变的,每次都被 PG 调用(即使在 where 子句中)
如果没有 r 记录参数(未使用),函数 rownum() 可能被过早地评估。这就是棘手的地方。想象一下,下面的 rownum() 函数:
create or replace function rownum(v_seq_name text default rownum_seq()) returns bigint as $$
declare
begin
return nextval(v_seq_name);
exception when undefined_table then
execute concat('create temporary sequence ',v_seq_name,' minvalue 1 increment by 1');
return nextval(v_seq_name);
end;
$$ language plpgsql volatile;
explain select ccy_code,rownum() from (select ccy_code from currency order by ccy_code desc) a where rownum()<10
Sort (cost=56.41..56.57 rows=65 width=4)
Sort Key: currency.ccy_code DESC
-> Seq Scan on currency (cost=0.00..54.45 rows=65 width=4)
Filter: (rownum('649aec1a-d512-4af0-87d8-23e8d8a9d982'::text) < 10)
PG 在订单前应用过滤器。该死! 使用第一个未使用的参数,我们强制 PG 在 filter 之前排序:
explain select * from (select ccy_code from currency order by ccy_code desc) a where rownum(a.*)<10;
Subquery Scan on a (cost=12.42..64.36 rows=65 width=4)
Filter: (rownum(a.*, 'seq_rownum_43b5c67f_dd64_4191_b29c_372061c848d6'::text) < 10)
-> Sort (cost=12.42..12.91 rows=196 width=4)
Sort Key: currency.ccy_code DESC
-> Seq Scan on currency (cost=0.00..4.96 rows=196 width=4)
优点:
缺点:
【讨论】:
如果你有唯一的密钥,你可以使用COUNT(*) OVER ( ORDER BY unique_key ) as ROWNUM
SELECT t.*, count(*) OVER (ORDER BY k ) ROWNUM
FROM yourtable t;
| k | n | rownum |
|---|-------|--------|
| a | TEST1 | 1 |
| b | TEST2 | 2 |
| c | TEST2 | 3 |
| d | TEST4 | 4 |
【讨论】:
使用限制子句,使用偏移量选择行号-1,所以如果你想获得第8行,那么使用:
限制 1 偏移量 7
【讨论】:
Postgresql 有限制。
Oracle 的代码:
select *
from
tbl
where rownum <= 1000;
在 Postgresql 的代码中相同:
select *
from
tbl
limit 1000
【讨论】:
我刚刚在 Postgres 9.1 中测试了一个接近 Oracle ROWNUM 的解决方案:
select row_number() over() as id, t.*
from information_schema.tables t;
【讨论】:
ORDER BY。
如果你只是想要一个号码回来试试这个。
create temp sequence temp_seq;
SELECT inline_v1.ROWNUM,inline_v1.c1
FROM
(
select nextval('temp_seq') as ROWNUM, c1
from sometable
)inline_v1;
您可以将 order by 添加到 inline_v1 SQL,以便您的 ROWNUM 对您的数据具有一定的顺序意义。
select nextval('temp_seq') as ROWNUM, c1
from sometable
ORDER BY c1 desc;
可能不是最快的,但如果您确实需要它们,这是一个选择。
【讨论】:
Postgresql > 8.4
SELECT
row_number() OVER (ORDER BY col1) AS i,
e.col1,
e.col2,
...
FROM ...
【讨论】:
Postgresql 没有相当于 Oracle 的 ROWNUM。 在许多情况下,您可以通过在查询中使用 LIMIT 和 OFFSET 来获得相同的结果。
【讨论】: