一网友在群内发来语句(语句内容已做处理)
SELECT *
FROM (SELECT
/*这儿有d0的30多列*/
d0.*
FROM d0 d0
WHERE d0.document_id IN
(SELECT d1.document_id
FROM d0 d1, s2 s2
WHERE d1.lid = s2.id
AND d1.dstat = 1
AND d1.fid = 'xxxxxxxx'
AND s2.sl <= 4
AND (d1.d_date IS NULL OR d1.d_date > '一串数字'))
ORDER BY d0.ct DESC)
WHERE rownum <= 50
该语句plan如下
Execution Plan
----------------------------------------------------------
Plan hash value: 3367774531
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50 | 181K| | 49417 (1)| 00:09:54 |
|* 1 | COUNT STOPKEY | | | | | | |
| 2 | VIEW | | 141K| 500M| | 49417 (1)| 00:09:54 |
|* 3 | SORT ORDER BY STOPKEY| | 141K| 32M| 33M| 49417 (1)| 00:09:54 |
|* 4 | HASH JOIN RIGHT SEMI| | 141K| 32M| 6352K| 42140 (1)| 00:08:26 |
| 5 | VIEW | VW_NSO_1 | 141K| 4693K| | 1782 (1)| 00:00:22 |
| 6 | MERGE JOIN | | 141K| 11M| | 1782 (1)| 00:00:22 |
|* 7 | INDEX RANGE SCAN | idx3 | 141K| 10M| | 1780 (1)| 00:00:22 |
|* 8 | SORT JOIN | | 14 | 84 | | 2 (50)| 00:00:01 |
|* 9 | INDEX RANGE SCAN| idx_id | 14 | 84 | | 1 (0)| 00:00:01 |
| 10 | TABLE ACCESS FULL | d0 | 1173K| 228M| | 28047 (1)| 00:05:37 |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=50)
3 - filter(ROWNUM<=50)
4 - access("d0"."DOCUMENT_ID"="DOCUMENT_ID")
7 - access("d1"."fid"='xxxxxxxx')
filter(TO_NUMBER("d1"."dstat")=1 AND
("d1"."d_date">一串数字 OR
"d1"."d_date" IS NULL))
8 - access("d1"."lid"="s2"."ID")
filter("d1"."lid"="s2"."ID")
9 - access("s2"."sl"<=4)
Statistics
----------------------------------------------------------
1952 recursive calls
0 db block gets
104014 consistent gets
103659 physical reads
0 redo size
11087 bytes sent via SQL*Net to client
552 bytes received via SQL*Net from client
5 SQL*Net roundtrips to/from client
18 sorts (memory)
0 sorts (disk)
50 rows processed
于是我问到d0.document_id是主建不?
网友回答:是。
于是问题来了。既然这个是主键,那么在上面的查询中,子查询里的d1完全是多余的。
大家看plan的id=4与id=8,表d0因为这种写法查询了两次。
于是我让网友把d1去掉,里面的条件放到外面来,d0作驱动走nl.
网友改后语句如下:
SELECT *
FROM (SELECT
/*这儿有d0的30多列*/
d0.*
FROM d0 d0
WHERE fid = 'xxxxxxxx'
AND dstat = '1'
AND (d0.d_date IS NULL OR d0.d_date > '一串数字')
AND EXISTS (SELECT
1
FROM s2 s2
WHERE d0.lid = s2.id
AND s2.sl <= 4)
ORDER BY d0.ct DESC)
WHERE rownum <= 50
到这儿应该没什么大问题了,可网友又来信息了。
说plan不固定,一会儿逻辑读40多,一会儿4W多。
于是我让网友用
dbms_xplan.display_cursor 看plan,把查到的plan复制出来做对比。结果发现驱动表在变,有时用的是d0,有时用的是s2(其实他前面用autotrace看也一样,只是他看的时候不够仔细)。
原因找到就好办了,加hint就可以了。
网友说加了 leading()
我一看,郁闷了,他leading()里用的是表的原名,而不是别名。nl加的也不对。
把正确的发给他,最后语句如下
SELECT *
FROM (SELECT /*+ leading(d0) */
/*这儿有d0的30多列*/
d0.*
FROM d0 d0
WHERE fid = 'xxxxxxxx'
AND dstat = '1'
AND (d0.d_date IS NULL OR d0.d_date > '一串数字')
AND EXISTS (SELECT /*+ nl_sj */
1
FROM s2 s2
WHERE d0.lid = s2.id
AND s2.sl <= 4)
ORDER BY d0.ct DESC)
WHERE rownum <= 50
大家在加hint的时候,一定要先用简单的语句做个实验,用熟了后才可以在大的查询里用。
hint没那么神奇,随意搞个就能用。就象你把跑鞋挂腰上仍然跑不快一样。
ok。到些结束。