How do I limit the number of rows returned by an Oracle query after ordering?

前端 未结 17 1545
夕颜
夕颜 2020-11-21 04:56

Is there a way to make an Oracle query behave like it contains a MySQL limit clause?

In MySQL, I can do this:

         


        
17条回答
  •  太阳男子
    2020-11-21 05:05

    As an extension of accepted answer Oracle internally uses ROW_NUMBER/RANK functions. OFFSET FETCH syntax is a syntax sugar.

    It could be observed by using DBMS_UTILITY.EXPAND_SQL_TEXT procedure:

    Preparing sample:

    CREATE TABLE rownum_order_test (
      val  NUMBER
    );
    
    INSERT ALL
      INTO rownum_order_test
    SELECT level
    FROM   dual
    CONNECT BY level <= 10;
    COMMIT;
    

    Query:

    SELECT val
    FROM   rownum_order_test
    ORDER BY val DESC
    FETCH FIRST 5 ROWS ONLY;
    

    is regular:

    SELECT "A1"."VAL" "VAL" 
    FROM  (SELECT "A2"."VAL" "VAL","A2"."VAL" "rowlimit_$_0",
                   ROW_NUMBER() OVER ( ORDER BY "A2"."VAL" DESC ) "rowlimit_$$_rownumber" 
          FROM "ROWNUM_ORDER_TEST" "A2") "A1" 
    WHERE "A1"."rowlimit_$$_rownumber"<=5 ORDER BY "A1"."rowlimit_$_0" DESC;
    

    db<>fiddle demo

    Fetching expanded SQL text:

    declare
      x VARCHAR2(1000);
    begin
     dbms_utility.expand_sql_text(
            input_sql_text => '
              SELECT val
              FROM   rownum_order_test
              ORDER BY val DESC
              FETCH FIRST 5 ROWS ONLY',
            output_sql_text => x);
    
      dbms_output.put_line(x);
    end;
    /
    

    WITH TIES is expanded as RANK:

    declare
      x VARCHAR2(1000);
    begin
     dbms_utility.expand_sql_text(
            input_sql_text => '
              SELECT val
              FROM   rownum_order_test
              ORDER BY val DESC
              FETCH FIRST 5 ROWS WITH TIES',
            output_sql_text => x);
    
      dbms_output.put_line(x);
    end;
    /
    
    SELECT "A1"."VAL" "VAL" 
    FROM  (SELECT "A2"."VAL" "VAL","A2"."VAL" "rowlimit_$_0",
                  RANK() OVER ( ORDER BY "A2"."VAL" DESC ) "rowlimit_$$_rank" 
           FROM "ROWNUM_ORDER_TEST" "A2") "A1" 
    WHERE "A1"."rowlimit_$$_rank"<=5 ORDER BY "A1"."rowlimit_$_0" DESC
    

    and offset:

    declare
      x VARCHAR2(1000);
    begin
     dbms_utility.expand_sql_text(
            input_sql_text => '
              SELECT val
    FROM   rownum_order_test
    ORDER BY val
    OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY',
            output_sql_text => x);
    
      dbms_output.put_line(x);
    end;
    /
    
    
    SELECT "A1"."VAL" "VAL" 
    FROM  (SELECT "A2"."VAL" "VAL","A2"."VAL" "rowlimit_$_0",
                 ROW_NUMBER() OVER ( ORDER BY "A2"."VAL") "rowlimit_$$_rownumber" 
           FROM "ROWNUM_ORDER_TEST" "A2") "A1" 
           WHERE "A1"."rowlimit_$$_rownumber"<=CASE  WHEN (4>=0) THEN FLOOR(TO_NUMBER(4)) 
                 ELSE 0 END +4 AND "A1"."rowlimit_$$_rownumber">4 
    ORDER BY "A1"."rowlimit_$_0"
    

提交回复
热议问题