2016-06-21 134 views
3

我试图按超过100万条记录的表中超过100K行的用户检索最后20行。当用户有少量记录时,查询性能很好(以毫秒为单位)。但需要2分钟以上的时间才能为拥有大约10K-100K记录的用户提取20条记录。Oracle 11g - 顶级N排序缓慢

这里的查询:

select * from (select * from TABLE1                            
     where USER_ID= 41063660 and                               
     COL1=0 and COL2 = 0 
    order by LAST_EVENT_DATE desc) where rownum <= 20 ; 

有上(USER_ID,COL1,COL2,LAST_EVENT_DATE DESC)索引(I_LASTEVENTDT)

这里是解释计划:

------------------------------------------------------------------------------------------------------------------------ 
| Id | Operation      | Name       | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | 
------------------------------------------------------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT    |        | 20 | 38960 |  | 66959 (1)| 00:13:24 | 
|* 1 | COUNT STOPKEY     |        |  |  |  |   |   | 
| 2 | VIEW       |        | 65500 | 121M|  | 66959 (1)| 00:13:24 | 
|* 3 | SORT ORDER BY STOPKEY  |        | 65500 | 96M| 102M| 66959 (1)| 00:13:24 | 
| 4 |  TABLE ACCESS BY INDEX ROWID| TABLE1      | 65500 | 96M|  | 47280 (1)| 00:09:28 | 
|* 5 |  INDEX RANGE SCAN   | I_LASTEVENTDT     | 65500 |  |  | 309 (0)| 00:00:04 | 
------------------------------------------------------------------------------------------------------------------------ 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - filter(ROWNUM<=20) 
    3 - filter(ROWNUM<=20) 
    5 - access("USER_ID"=41063660 AND "COL1"=0 AND 
       "COL2"=0) 

我试着按照http://use-the-index-luke.com/sql/sorting-grouping/indexed-order-by

给出的例子试过(USER_ID,COL1,COL2)和(LAST_EVENT_DT DESC)上创建一个单独的索引,并尝试索引(USER_ID,LAST_EVENT_DT DESC)。尽管后一个指数摆脱了排序顺序,但两者的表现都更差。

如何从此查询中获得更好的性能?

在此先感谢。

+0

解释计划说查询只需要15秒65K行(我希望我不误读)。你怎么说这需要2分钟?在查询之前和之后你会做更多的处理吗? – sstan

+0

该计划显示13分钟,而不是15秒,只是一个估计。它需要2分钟+取决于获取的行数,当我从我的应用程序运行它。 – user2755442

+0

糟糕。谢谢澄清。仅用于调查目的,您是否尝试在查询中添加“FIRST_ROWS”提示?它是否改变了解释计划? – sstan

回答

0

第一次尝试这样的:

SELECT * 
FROM 
    (SELECT *, ROW_NUMBER() OVER (ORDER BY last_event_date desc) R 
    FROM table1 
    WHERE user_id = 41063660 
    AND col1 = 0 
    AND col2 = 0) 
WHERE R <= 20; 

如果证明不是要快,试试:

SELECT * 
FROM table1, 
    (SELECT last_event_date, ROW_NUMBER() OVER (ORDER BY last_event_date desc) R 
    FROM table1 
    WHERE user_id = 41063660 
    AND col1 = 0 
    AND col2 = 0) sub 
WHERE table1.user_id = 41063660 
AND table1.col1 = 0 
AND table1.col2 = 0 
AND sub.R = 20 
AND table1.last_event_date >= sub.last_event_date 
AND ROWNUM <= 20; 

有可能是一个更直接的方式写出来 - 我没有一个Oracle实例可供我试用。

另一种方法是实现一个SQL函数,只是给出某些键的第20(或第N)行的日期。然后以类似于我的第二个示例但没有子查询的方式调用该SQL函数。

0

我认为你应该寻找一个索引快速全面扫描,不要排序整个行。另外,我试图只获取少量的记录(20),并将它们再次连接到主表。

with Dates as (
    select /*+ Materialize */ LAST_EVENT_DATE 
     from (select LAST_EVENT_DATE 
         from TABLE1                            
        where USER_ID= 41063660 
         and COL1=0 
         and COL2 = 0 
        order by LAST_EVENT_DATE desc) 
     where rownum <= 20) 
select t2.* 
    from (
    select t1.* 
     from TABLE1 t1 join Dates on t1.LAST_EVENT_DATE >= Dates.LAST_EVENT_DATE 
           and t1.USER_ID= 41063660 
           and t1.COL1=0 
           and t1.COL2 = 0 
    order by t1.LAST_EVENT_DATE desc) as t2 
where rownum <= 20;