ROWNUM
OracleLIMIT
では MySQL とは異なります。
MySQLでは、
このLIMIT
句は、SELECT
ステートメントによって返される行数を制限するために使用できます... 2 つの引数を使用すると、最初の引数は返す最初の行のオフセットを指定し、2 番目の引数は返す行の最大数を指定します。最初の行のオフセットは 0 (1 ではない) です。
SELECT * FROM tbl LIMIT 5,10; # Retrieve rows 6-15
オラクルでは、
クエリによって返される行ごとに、ROWNUM
疑似列は、Oracle がテーブルまたは結合された行のセットから行を選択する順序を示す数値を返します。選択された最初の行ROWNUM
は 1 で、2 番目の行は 2 というように続きます。
SELECT *
FROM (SELECT * FROM employees ORDER BY employee_id)
WHERE ROWNUM < 11;
前の例では、ROWNUM
値は最上位SELECT
ステートメントの値であるため、サブクエリで行が employee_id によって既に並べ替えられた後に生成されます。
したがって、コードの問題は次のとおりです。
WHERE ROWNUM BETWEEN :startrow AND :perpage
ROWNUM
「行数」インジケーターではなく、行インデックスインジケーターと同様に、論理的に正しくありません。ROWNUM BETWEEN a AND b
a から b の順に行を返すことを意味します。たとえば、行 #1 から行 #3 であり、行 #1 と 3 行下ではありません。
したがって、ROWNUM BETWEEN a AND b
機能する場合でも(実際には機能しません)、クエリの「ページ2」を取得するには、論理的に必要ROWNUM BETWEEN :startrow AND :endrow
ですROWNUM BETWEEN 4 AND 6
。
ROWNUM BETWEEN :startrow AND :endrow
は、選択されたセット内ROWNUM
の行の順序を表す疑似列であるため、1 より大きい開始行では実際には機能しません。
たとえば、実行するSELECT...WHERE ROWNUM BETWEEN 4 AND 6
と、
- 最初の一致で、Oracle は「rownum は 4 から 6 になるか?」と考えます。まだ行が選択されていないため、選択されている場合
ROWNUM
は 1 になるため、Oracle はそれを破棄します。
- 2戦目「これは4対6で?」いいえ、行が選択されていないため、これは 1 のままです。
- 3 番目の一致、同じ。
- ...
結果はまったくありません。
望ましい出力を得るには、次のように、クエリをサブクエリでラップし、外側の行番号を確認する必要があります。
SELECT * FROM
(SELECT ROWNUM rn, t.*
FROM
(SELECT ...
FROM ...
WHERE ...
ORDER BY ...) t
)
WHERE rn>=:startrow AND rn<=:endrow
行の順序を確認する前に、行の順序が確定されていることを保証します。
- また、コーディング スタイルについては、OCI は準備済みステートメントと変数バインディングをサポートしているため、それを使用する必要があります。
バインドにより、データベースはステートメントのコンテキストとステートメントの以前の実行からのキャッシュを再利用できます...バインド変数に関連付けられたデータが SQL ステートメントの一部として扱われることがないため、バインドにより SQL インジェクションの懸念が軽減されます。
$statement=oci_parse("... WHERE rn>=:start AND rn<=:end");
oci_bind_by_name($statement,":start",$start,-1,SQLT_INT);
oci_bind_by_name($statement,":end",intval($start+$per_page-1),-1,SQLT_INT);
oci_execute($statement);