【问题标题】:Rownum in postgresqlpostgresql中的Rownum
【发布时间】:2011-04-26 23:00:37
【问题描述】:

postgresql 中有什么方法可以模拟 rownum 吗?

【问题讨论】:

    标签: postgresql row-number rownum


    【解决方案1】:

    我认为可以使用临时序列来模仿 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)
    

    优点:

    • 用作表达式或在 where 子句中
    • 易于使用:只需传递第一条记录。*您在 from 中拥有

    缺点:

    • 为遇到的每个 rownum() 创建一个临时序列,但在会话结束时将其删除。
    • 性能(讨论,row_number() over () 与 nextval)

    【讨论】:

      【解决方案2】:

      如果你有唯一的密钥,你可以使用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 |
      

      DEMO

      【讨论】:

      • 这正是我的需要,它解决了我的问题。谢谢!!
      【解决方案3】:

      使用限制子句,使用偏移量选择行号-1,所以如果你想获得第8行,那么使用:

      限制 1 偏移量 7

      【讨论】:

        【解决方案4】:

        Postgresql 有限制。

        Oracle 的代码:

        select *
        from
          tbl
        where rownum <= 1000;
        

        在 Postgresql 的代码中相同:

        select *
        from
          tbl
        limit 1000
        

        【讨论】:

        【解决方案5】:

        我刚刚在 Postgres 9.1 中测试了一个接近 Oracle ROWNUM 的解决方案:

        select row_number() over() as id, t.*
        from information_schema.tables t;
        

        【讨论】:

        • 请注意,这不会尊重外部查询中的ORDER BY
        【解决方案6】:

        如果你只是想要一个号码回来试试这个。

        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;
        

        可能不是最快的,但如果您确实需要它们,这是一个选择。

        【讨论】:

          【解决方案7】:

          Postgresql > 8.4

          SELECT 
              row_number() OVER (ORDER BY col1) AS i, 
              e.col1, 
              e.col2, 
              ... 
          FROM ... 
          

          【讨论】:

          • 小心。这与oracle的rownum类似,但略有不同。 Oracle 的 rownum 是在从磁盘读取行时分配的,而此 row_number() 是根据您的 OVER 中的内容分配的
          • 速度差异如何?
          【解决方案8】:

          Postgresql 没有相当于 Oracle 的 ROWNUM。 在许多情况下,您可以通过在查询中使用 LIMIT 和 OFFSET 来获得相同的结果。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-04-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-10-19
            • 1970-01-01
            • 2011-10-04
            相关资源
            最近更新 更多