在Oracle 12中,我们最终有一个限制功能,所以我们可以简单地
select distinct columnname from mytable fetch first n rows only;
但是,我目前仍然坚持使用以前的版本,并想知道如何实现这一结果。
理想情况下,查询应该立即返回行 ,即使对于一个巨大的表也是如此。它应该在找到N distinct后立即返回结果,而不是处理所有行。
答案 0 :(得分:2)
我认为使用
.. where rownum < XXX
应该有效
也许
select * from (select distinct columnname from mytable) where rownum < XXX
答案 1 :(得分:2)
没有任何版本的Oracle具有本机语法,可以以最佳方式返回不同的前N个。必须使用PL / SQL流水线函数手动创建此功能。
此脚本创建一个包含一列的表,大约有1亿行,占用大约1GB的空间。
--drop table mytable purge;
create table mytable(columnname number not null) nologging;
insert /*+ append */ into mytable
select level from dual connect by level <= 100000;
commit;
begin
for i in 1 .. 10 loop
insert /*+ append */ into mytable select * from mytable;
commit;
end loop;
end;
/
begin
dbms_stats.gather_table_stats(user, 'MYTABLE');
end;
/
--1.25GB.
select bytes/1024/1024/1024 gb from dba_segments where segment_name = 'MYTABLE';
新的12c语法在大约20秒内持续运行以返回少量行:
select distinct columnname from mytable fetch first 10 rows only;
该语句读取整个表,散列整个表,然后抓取前N行:
explain plan for
select distinct columnname from mytable fetch first 10 rows only;
select * from table(dbms_xplan.display(format => 'basic'));
Plan hash value: 239985407
------------------------------------------
| Id | Operation | Name |
------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | VIEW | |
| 2 | WINDOW NOSORT STOPKEY| |
| 3 | VIEW | |
| 4 | HASH UNIQUE | |
| 5 | TABLE ACCESS FULL | MYTABLE |
------------------------------------------
由Ed Heal创建的Oracle 11g版本令人惊讶地工作更好!它运行大约12秒钟。
select * from (select distinct columnname from mytable) where rownum < 10;
即使速度更快,12秒仍然很糟糕。无论我的CPU或I / O性能如何,如果算法需要几秒而不是几毫秒,那么算法必定是错误的。
事实上,这个计划看起来好一点。它在计划中有SORT GROUP BY STOPKEY
个低点。这会在处理所有内容之前停止查询。但它仍然停止太晚了。 (也许Oracle仍在阅读整个表格,但只对它进行排序?)
explain plan for
select * from (select distinct columnname from mytable) where rownum < 10;
select * from table(dbms_xplan.display(format => 'basic'));
Plan hash value: 3842480186
-------------------------------------------
| Id | Operation | Name |
-------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | COUNT STOPKEY | |
| 2 | VIEW | |
| 3 | SORT GROUP BY STOPKEY| |
| 4 | TABLE ACCESS FULL | MYTABLE |
-------------------------------------------
由于几个原因,这是一个丑陋的解决方案。它需要不同结果集的新代码和对象。它可能无法很好地扩展 - 该函数有一个集合来存储以前的结果,如果该集合变得庞大会发生什么?
每种不同的结果类型都需要新对象:
--Create an object to hold a record with the result columns.
--(Not necessary for this simple example since there's only one column, but will
-- be necessary if there are multiple columns.)
create or replace type columnname_rec is object
(
columnname number
);
--Create an object to hold a table of the records.
create or replace type columnname_tab is table of columnname_rec;
另一个返回不同类型的函数:
--Function that returns the distinct Top N as soon as they are found.
create or replace function fast_distinct_top_n(p_n number, p_cursor in sys_refcursor) return columnname_tab pipelined is
v_columnname number;
v_distinct_count number := 0;
type previous_values_type is table of varchar2(4000) index by varchar2(4000);
v_previous_values previous_values_type;
begin
loop
--Get new value.
fetch p_cursor into v_columnname;
--If the new value does not exist...
if not v_previous_values.exists(v_columnname) then
--Save the new value.
v_previous_values(v_columnname) := v_columnname;
--Increment the counter.
v_distinct_count := v_distinct_count + 1;
--Return the value
pipe row(columnname_rec(v_columnname));
--Exit if the counter is more than the top N.
exit when v_distinct_count >= p_n;
end if;
end loop;
end;
/
但最后我们创建了一个查询,以毫秒为单位返回不同的前N个结果。
select * from table(fast_distinct_top_n(10, cursor(select * from mytable)));
如果您无法创建如此多的对象,则可能有一种方法可以使用Method4使其成为通用对象。但是这个解决方案仍然很复杂。