web-dev-qa-db-ja.com

注文後にOracleクエリから返される行数を制限するにはどうすればよいですか?

OracleクエリをMySQL limit句を含むように動作させる方法はありますか?

MySQLでは、これを行うことができます。

select * 
from sometable
order by name
limit 20,10

21行目から30行目までを取得します(最初の20行をスキップし、次の10行を指定します)。行はorder byの後で選択されるので、実際にはアルファベット順で20番目の名前から始まります。

Oracleでは、人々が言及するのはrownum疑似列だけですが、の前にorder byが評価されます。

select * 
from sometable
where rownum <= 10
order by name

名前順に並べられた10行のランダムなセットを返します。これは通常私が望むものではありません。オフセットを指定することもできません。

916
Mathieu Longtin

Oracle 12c R1(12.1)以降では、行制限句 です。使い慣れたLIMIT構文は使用しませんが、より多くのオプションを使用するとより効果的に作業を実行できます。あなたはここで 完全な構文を見つけることができます

元の質問に答えるために、これがクエリです:

SELECT * 
FROM   sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

(以前のバージョンのOracleについては、この質問の他の回答を参照してください)


例:

以下の例は、リンク腐敗を防ぐために、 リンクされたページ から引用されています。

セットアップ

CREATE TABLE rownum_order_test (
  val  NUMBER
);

INSERT ALL
  INTO rownum_order_test
SELECT level
FROM   dual
CONNECT BY level <= 10;

COMMIT;

表の中身は?

SELECT val
FROM   rownum_order_test
ORDER BY val;

       VAL
----------
         1
         1
         2
         2
         3
         3
         4
         4
         5
         5
         6
         6
         7
         7
         8
         8
         9
         9
        10
        10

20 rows selected.

最初のN行を取得する

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

       VAL
----------
        10
        10
         9
         9
         8

5 rows selected.

Nの場合、最初のN行を取得します番目 行に関係がある場合は、すべての関係のある行を取得します

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

       VAL
----------
        10
        10
         9
         9
         8
         8

6 rows selected.

トップx%行

SELECT val
FROM   rownum_order_test
ORDER BY val
FETCH FIRST 20 PERCENT ROWS ONLY;

       VAL
----------
         1
         1
         2
         2

4 rows selected.

オフセットを使用して、ページ付けに非常に役立ちます

SELECT val
FROM   rownum_order_test
ORDER BY val
OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY;

       VAL
----------
         3
         3
         4
         4

4 rows selected.

オフセットとパーセントを組み合わせることができます

SELECT val
FROM   rownum_order_test
ORDER BY val
OFFSET 4 ROWS FETCH NEXT 20 PERCENT ROWS ONLY;

       VAL
----------
         3
         3
         4
         4

4 rows selected.
456
sampathsris

あなたはこのようなサブクエリを使うことができます

select *
from  
( select * 
  from emp 
  order by sal desc ) 
where ROWNUM <= 5;

詳細については、Oracle/AskTomでROWNUMに関するトピック および結果を制限する も参照してください。

更新:下限と上限の両方で結果を制限するために、事情でもう少し肥大する

select * from 
( select a.*, ROWNUM rnum from 
  ( <your_query_goes_here, with order by> ) a 
  where ROWNUM <= :MAX_ROW_TO_FETCH )
where rnum  >= :MIN_ROW_TO_FETCH;

(指定されたAskTom記事からコピー)

Update 2:Oracle 12c(12.1)以降では、行数を制限したりオフセットから開始するための構文があります。

SELECT * 
FROM   sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

他の例については この答え をご覧ください。ヒントをくれたKrumiaに感謝します。

753
Kosi2801

私は以下の方法でパフォーマンステストを行いました。

アスクトム

select * from (
  select a.*, ROWNUM rnum from (
    <select statement with order by clause>
  ) a where rownum <= MAX_ROW
) where rnum >= MIN_ROW

分析的

select * from (
  <select statement with order by clause>
) where myrow between MIN_ROW and MAX_ROW

ショートオルタナティブ

select * from (
  select statement, rownum as RN with order by clause
) where a.rn >= MIN_ROW and a.rn <= MAX_ROW

結果

テーブルには1000万件のレコードがあり、ソートはインデックスのないdatetime行にあります。

  • Explain planは3つの選択すべてに対して同じ値を示しました(323168)
  • しかし勝者はAskTomです(分析的なフォローはすぐ後ろにあります)

最初の10行を選択すると

  • AskTom:28-30秒
  • 分析:33〜37秒
  • 短い選択肢:110〜140秒

100,000から100,010までの行を選択します。

  • AskTom:60秒
  • 分析:100秒

9,000,000〜9,000,010の行を選択します。

  • AskTom:130秒
  • 分析:150秒
173
zeldi

入れ子になったクエリが1つだけの分析ソリューション

SELECT * FROM
(
   SELECT t.*, Row_Number() OVER (ORDER BY name) MyRow FROM sometable t
) 
WHERE MyRow BETWEEN 10 AND 20;

Rank()の代わりにRow_Number()を使用することもできますが、nameの値が重複していると、予想よりも多くのレコードが返される可能性があります。

53
Leigh Riffel

Oracle 12cの場合( SQLリファレンス の行制限句を参照)。

SELECT * 
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
28
beldaz

順序付きのページネーションクエリは、Oracleでは本当に扱いにくいです。

Oracleは、データベースがテーブルまたは一連の結合ビューから行を選択する順序を示す番号を返すROWNUM疑似列を提供します。

ROWNUMは多くの人を困らせる疑似列です。 ROWNUM値は、行に永続的に割り当てられるわけではありません(これはよくある誤解です)。 ROWNUM値が実際に割り当てられている場合は混乱するかもしれません。 ROWNUM値は、クエリの行フィルタ述語を渡した後に割り当てられますが、クエリ集計またはソートの前に割り当てられます。

さらに、ROWNUM値は割り当てられた後にのみ増加します。

これが、後続のクエリが行を返さない理由です。

 select * 
 from (select *
       from some_table
       order by some_column)
 where ROWNUM <= 4 and ROWNUM > 1; 

照会結果の最初の行はROWNUM> 1述部を通過しないので、ROWNUMは2に増分しません。このため、ROWNUM値が1より大きくなることはなく、その結果、照会は行を戻しません。

正しく定義されたクエリは次のようになります。

select *
from (select *, ROWNUM rnum
      from (select *
            from skijump_results
            order by points)
      where ROWNUM <= 4)
where rnum > 1; 

Vertabelo ブログのページネーションクエリについてもっと知りたい:

11
Bartek

少ないSELECTステートメント。また、パフォーマンスが低下します。クレジットに:[email protected]

SELECT *
    FROM   (SELECT t.*,
                   rownum AS rn
            FROM   shhospede t) a
    WHERE  a.rn >= in_first
    AND    a.rn <= in_first;
6

Oracle 12Cを使用していない場合は、以下のようにTOP Nクエリを使用できます。

SELECT *
 FROM
   ( SELECT rownum rnum
          , a.*
       FROM sometable a 
   ORDER BY name
   )
WHERE rnum BETWEEN 10 AND 20;

次のようにwith句でfrom句を移動することもできます。

WITH b AS
( SELECT rownum rnum
      , a.* 
   FROM sometable a ORDER BY name
) 
SELECT * FROM b 
WHERE rnum BETWEEN 10 AND 20;

ここでは、実際にはインラインビューを作成し、rownumの名前をrnumに変更しています。あなたはフィルタ基準としてメインクエリでrnumを使うことができます。

2
sandi

受け入れられた回答 の拡張として、Oracleは内部でROW_NUMBER/RANK関数を使用します。 OFFSET FETCH構文は構文シュガーです。

DBMS_UTILITY.EXPAND_SQL_TEXTプロシージャを使用して確認できます。

サンプルの準備:

CREATE TABLE rownum_order_test (
  val  NUMBER
);

INSERT ALL
  INTO rownum_order_test
SELECT level
FROM   dual
CONNECT BY level <= 10;
COMMIT;

クエリ:

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

定期的です:

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

展開されたSQLテキストの取得:

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 TIESRANKとして展開されます:

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

およびオフセット:

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"
2
Lukasz Szozda
select * FROM (SELECT 
   ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
 FROM EMP ) EMP  where ROWID=5

値よりも大きい

select * FROM (SELECT 
       ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
     FROM EMP ) EMP  where ROWID>5

値よりも少ない

select * FROM (SELECT 
       ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
     FROM EMP ) EMP  where ROWID=5
2
Mehul Akabari

SQL標準

この記事 で説明したように、SQL:2008 Standardは次の構文を提供してSQL結果セットを制限します。

SELECT
    title
FROM
    post
ORDER BY
    id DESC
FETCH FIRST 50 ROWS ONLY

Oracle 11gおよびそれ以前のバージョン

バージョン12cより前のTop-Nレコードを取得するには、派生テーブルとROWNUM疑似列を使用する必要がありました。

SELECT *
FROM (
    SELECT
        title
    FROM
        post
    ORDER BY
        id DESC
)
WHERE ROWNUM <= 50
1
Vlad Mihalcea

12cに対して検証されたOracle 1z0-047試験の準備を始めました。準備中に、 'FETCH FIRST'として知られる12cの拡張機能に出くわしました。いくつかのオプションがそれで利用可能です

- FETCH FIRST n ROWS ONLY
 - OFFSET n ROWS FETCH NEXT N1 ROWS ONLY // leave the n rows and display next N1 rows
 - n % rows via FETCH FIRST N PERCENT ROWS ONLY

例:

Select * from XYZ a
order by a.pqr
FETCH FIRST 10 ROWS ONLY
1
arjun gaur

問合せによって戻される各行に対して、ROWNUM疑似列は、Oracleが表または一連の結合行から行を選択する順序を示す番号を返します。選択された最初の行のROWNUMは1、2番目の行は2、というようになります。

  SELECT * FROM sometable1 so
    WHERE so.id IN (
    SELECT so2.id from sometable2 so2
    WHERE ROWNUM <=5
    )
    AND ORDER BY so.somefield AND ROWNUM <= 100 

私はこれをOracleサーバー11.2.0.1.0に実装しました

0
Sumesh TG