我正在使用Oracle 10g和以下范例来获取15个结果的页面(因此当用户查看搜索结果的第2页时,他们会看到记录16-30)。
select *
from
( select rownum rnum, a.*
from (my_query) a
where rownum <= 30 )
where rnum > 15;
现在我必须运行一个单独的SQL语句来对“my_query”执行“select count”以获取my_query的结果总数(以便我可以向用户显示并使用它计算总页数等)。
有没有办法在不通过第二个查询执行此操作的情况下获取结果总数,即从上面的查询中获取结果?我已经尝试添加“max(rownum)”,但它似乎不起作用(我得到一个错误[ORA-01747]似乎表明它不喜欢我在组中有关键字rownum)。
我希望从原始查询中获取此内容而不是在单独的SQL语句中执行此操作的理由是“my_query”是一个昂贵的查询,所以我宁愿不运行它两次(一次得到计数,一次如果我不需要,那么获取数据页面;但是,如果可能的话,我可以在单个查询中获得结果数量(同时获取我需要的数据页面)的任何解决方案都不应该增加很多额外的开销。请指教。
这正是我正在尝试做的事情,因为我认为它不喜欢我在组中使用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
---------编辑-------- 请注意,基于第一个评论我尝试了以下似乎工作。我不知道它与其他解决方案相比表现如何(我正在寻找满足我的要求但表现最佳的解决方案)。例如,当我运行它需要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
解释计划从执行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
答案 0 :(得分:20)
我认为您必须将查询修改为类似的内容,以便在“单个”查询中获取所需的所有信息。
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
条件的记录的数量。 / p>
如果您无法接受此查询的性能,或执行2个单独的查询,您可能应该考虑像 FrustratedWithFormsDesigner 在他/她关于缓存计数的评论中提出的解决方案记录。
如果您定期使用数据库,我建议您获取SQL Cookbook的副本。这是一本特别的书,有很多有用的提示。
答案 1 :(得分:2)
只是一个建议:
你可以考虑谷歌“ 1-10大约13,000,000个结果”的方法 - 运行COUNT(*)作为原始查询的快速样本。我在此假设给定的XYZ
最多只有一个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
显然,样本将是非常不准确和可变的,因此它取决于是否合适的要求。
答案 2 :(得分:1)
这有用吗?
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;
答案 3 :(得分:1)
不,如果没有运行查询两次,或者运行一次并在开始显示它们之前获取并缓存所有行来计算它们,则无法执行此操作。这两者都不可取,特别是如果您的查询很昂贵或可能返回很多行。
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);
答案 4 :(得分:1)
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
此查询将计算您需要获取的页面组数,并将数据作为一个查询获取。
从结果集中,一次处理15行。最后一组行可能短于15。
答案 5 :(得分:1)
另一个解决方案是创建一个物化视图来维护ABC.XYZ_ID
的每个值的计数 - 这样就可以减轻计数到表中插入/更新/删除行的进程的负担。
答案 6 :(得分:0)
以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结果。
答案 7 :(得分:-1)
对于Oracle 12c,这将起作用:
select a.*, count(*)over() from mytable a offset 10 fetch first 10 rows only