Oracle 10gと次のパラダイムを使用して、15ページの結果のページを一度に取得しています(ユーザーが検索結果の2ページを表示しているときに、レコード16〜30が表示されます)。
select *
from
( select rownum rnum, a.*
from (my_query) a
where rownum <= 30 )
where rnum > 15;
現在、my_queryの結果の総数を取得するために、別のSQLステートメントを実行して「my_query」で「select count」を実行する必要があります(ユーザーに表示してそれを使用して把握できるようにするため)総ページ数など)。
2番目のクエリを使用せずに、つまり上記のクエリから取得することによって、結果の総数を取得する方法はありますか? 「max(rownum)」を追加しようとしましたが、機能していないようです(グループにキーワードrownumを使用するのが好きではないことを示すエラー[ORA-01747]が表示されます)。
別のSQLステートメントで実行するのではなく、元のクエリからこれを取得したいという私の理論的根拠は、 "my_query"は負荷の高いクエリであるため、2回実行するのではなく(一度カウントを取得し、1回取得して)データのページ)必要がない場合;ただし、単一のクエリ内から結果の数を取得する(同時に、必要なデータのページを取得する)ために思いつく可能性のある解決策は、可能であれば、追加のオーバーヘッドがあったとしても、それほど追加すべきではありません。お知らせ下さい。
これはまさに私が行おうとしているものであり、ROWNUMをグループ化するのが嫌いなので、ORA-01747エラーが表示されます。注:max(ROWNUM)を使用しない別の解決策があるが、それ以外の解決策がある場合でも、それで問題ありません。このソリューションは、何が機能するかについての私の最初の考えでした。
SELECT * FROM (SELECT r.*, ROWNUM RNUM, max(ROWNUM)
FROM (SELECT t0.ABC_SEQ_ID AS c0, t0.FIRST_NAME, t0.LAST_NAME, t1.SCORE
FROM ABC t0, XYZ t1
WHERE (t0.XYZ_ID = 751) AND
t0.XYZ_ID = t1.XYZ_ID
ORDER BY t0.RANK ASC) r WHERE ROWNUM <= 30 GROUP BY r.*, ROWNUM) WHERE RNUM > 15
--------- EDIT --------注、最初のコメントに基づいて、機能しているように見える以下を試しました。ただし、他のソリューションと比較してパフォーマンスがどの程度かはわかりません(自分の要件を満たしながら最高のパフォーマンスを発揮するソリューションを探しています)。たとえば、これを実行すると、16秒かかります。 COUNT(*)OVER()RESULT_COUNTを取り出すと、わずか7秒かかります。
SELECT * FROM (SELECT r.*, ROWNUM RNUM, )
FROM (SELECT COUNT(*) OVER () RESULT_COUNT,
t0.ABC_SEQ_ID AS c0, t0.FIRST_NAME, t1.SCORE
FROM ABC t0, XYZ t1
WHERE (t0.XYZ_ID = 751) AND t0.XYZ_ID = t1.XYZ_ID
ORDER BY t0.RANK ASC) r WHERE ROWNUM <= 30) WHERE RNUM > 1
EXPLAIN PLANは、SORT(ORDER BY STOP KEY)を実行することから、WINDOW(SORT)を実行するように変更されます。
前:
SELECT STATEMENT ()
COUNT (STOPKEY)
VIEW ()
SORT (ORDER BY STOPKEY)
NESTED LOOPS ()
TABLE ACCESS (BY INDEX ROWID) XYZ
INDEX (UNIQUE SCAN) XYZ_ID
TABLE ACCESS (FULL) ABC
後:
SELECT STATEMENT ()
COUNT (STOPKEY)
VIEW ()
WINDOW (SORT)
NESTED LOOPS ()
TABLE ACCESS (BY INDEX ROWID) XYZ
INDEX (UNIQUE SCAN) XYZ_ID
TABLE ACCESS (FULL) ABC
「単一」クエリで必要なすべての情報を取得するには、クエリをこのようなものに変更する必要があると思います。
_SELECT *
FROM (SELECT r.*, ROWNUM RNUM, COUNT(*) OVER () RESULT_COUNT
FROM (SELECT t0.ABC_SEQ_ID AS c0, t0.FIRST_NAME, t1.SCORE
FROM ABC t0, XYZ t1
WHERE (t0.XYZ_ID = 751)
AND t0.XYZ_ID = t1.XYZ_ID
ORDER BY t0.RANK ASC) R)
WHERE RNUM between 1 and 15
_
その理由は、COUNT(*) OVER()
ウィンドウ関数がWHERE
句の後に評価されるため、レコードの総数ではなく、_ROWNUM <= 30
_条件を満たすレコードの数が得られるためです。
このクエリのパフォーマンスを受け入れることができない場合、または2つの別々のクエリを実行する場合は、おそらく彼のFrustratedWithFormsDesignerによって提案されたようなソリューションについて考える必要があります/レコード数のキャッシュに関するコメント。
データベースを定期的に使用している場合は、 SQL Cookbook のコピーを入手することをお勧めします。役立つヒントが満載の逸品です。
ただの提案:
Google "約13,000,000の結果のうち1-1"アプローチ-COUNT(*)を元のクエリのクイックサンプルとして実行することを検討できます。ここでは、指定されたXYZ
に対して最大で1つのABC
があると仮定しました:
SELECT *
FROM (SELECT r.*, ROWNUM RNUM,
(SELECT COUNT(*) * 100
FROM ABC SAMPLE(1) t0
WHERE (t0.XYZ_ID = 751)
) RESULT_COUNT
FROM (SELECT t0.ABC_SEQ_ID AS c0, t0.FIRST_NAME, t1.SCORE
FROM ABC t0, XYZ t1
WHERE (t0.XYZ_ID = 751)
AND t0.XYZ_ID = t1.XYZ_ID
ORDER BY t0.RANK ASC) R)
WHERE RNUM between 1 and 15
明らかに、サンプルは非常に不正確で変動しやすいため、これが適切かどうかは要件によって異なります。
WITH
base AS
(
SELECT ROWNUM RNUM, A.*
FROM (SELECT * FROM some_table WHERE some_condition) A
)
SELECT FLOOR(((SELECT COUNT(*) FROM base) / 15) + 1) TOTAL_PAGES_TO_FETCH,
((ROWNUM - MOD(ROWNUM, 15)) / 15) + 1 PAGE_TO_FETCH,
B.*
FROM base B
このクエリは、フェッチする必要があるページのグループ数を計算し、データを1つのクエリとしてフェッチします。
結果セットから、一度に15行を処理します。最後の行のセットは、15より短くなる場合があります。
いいえ、クエリを2回実行するか、クエリを1回実行してall行を取得およびキャッシュしてからでないと、表示を開始する前に行をカウントできません。特にクエリが高価であるか、潜在的に多くの行を返す場合は、どちらも望ましくありません。
Oracle独自のApplication Express(Apex)ツールは、ページ付けオプションの選択肢を提供します。
オプション3(好み)の疑似PL/SQLは次のようになります。
l_total := 15;
for r in
( select *
from
( select rownum rnum, a.*
from (my_query) a
)
where rnum > 15
)
loop
l_total := l_total+1;
if runum <= 30 then
print_it;
end if;
end loop;
show_page_info (15, 30, l_total);
これは機能しますか?
select *
from
( select rownum rnum, a.*, b.total
from (my_query) a, (select count(*) over () total from my_query) b
where rownum <= 30 )
where rnum > 15;
別の解決策は、ABC.XYZ_ID
の各値のカウントを維持するマテリアライズドビューを作成することです。これにより、テーブルの行を挿入/更新/削除するプロセスにカウントを取得する負担がかかります。
EvilTeachの答えを基にするには:
WITH
base AS
(
SELECT (ROWNUM - 1) RNUM, A.*
FROM (SELECT * FROM some_table WHERE some_condition) A
)
SELECT V.* FROM (
SELECT FLOOR(((SELECT COUNT(*) FROM base) / 15) + 1) TOTAL_PAGES_TO_FETCH,
((RNUM - MOD(RNUM, 15)) / 15) + 1 PAGE_TO_FETCH,
B.*
FROM base B
) V
WHERE V.PAGE_TO_FETCH = xx
ここで、XXは必要なページです。
上記のソリューションには、最初のページがPAGE_SIZE-1の結果を返す原因となった元のコードに対する小さなバグ修正が含まれています。