查询排序最多的SQL语句:
WITH sql_workarea AS
(SELECT sql_id || '_' || child_number sql_id_child, operation_type operation, last_execution last_exec, round(active_time / 1000000, 2) seconds, optimal_executions || '/' || multipasses_executions olm, '' || substr(sql_text, 1, 155) sql_text, rank() over(ORDER BY active_time DESC) ranking FROM v$sql_workarea JOIN v$sql USING (sql_id, child_number))SELECT sql_id_child "SQL ID-CHILD", seconds, operation, last_exec, olm "O/1/M", sql_text FROM sql_workarea WHERE ranking <= 10 ORDER BY ranking;10033跟踪排序:
alter session set tracefile_identifier=e10033;
alter session set events '10033 trace name context forever,level 1';
---- Sort Statistics ------------------------------Initial runs 14Number of merges 1Input records 55500Output records 55500Disk blocks 1st pass 1467Total disk blocks used 1451Total number of comparisons performed 699074 Comparisons performed by in-memory sort 485849 Comparisons performed during merge 213212 Comparisons while searching for key in-memory 13Number of seeks in final run 55500Temp segments allocated 1Extents allocated 12Uses version 2 sortUses asynchronous IO ---- Run Directory Statistics ----Run directory block reads (buffer cache) 15Block pins (for run directory) 1Block repins (for run directory) 14Maximum input run size (in blocks) 109Minimum input run size (in blocks) 32Average input run size (in blocks) 104 ---- Direct Write Statistics -----Write slot size 49152Write slots used during in-memory sort 2Number of direct writes 247Num blocks written (with direct write) 1449Block pins (for sort records) 1449Waits for async writes 199 ---- Direct Read Statistics ------Size of read slots for output 32768Number of read slots for output 32Number of direct sync reads 30Number of blocks read synchronously 95Number of direct async reads 343Number of blocks read asynchronously 1354使用索引来规避排序
如果在order by字句中的部分或者全部列上存在索引,oracle有可能使用索引来按照要求的顺序获取记录,因此也避免了排序操作。
假如索引是出现在与orde by字句里的列相同的列上,oracle可以直接从索引中按照索引排序的顺序读取记录,然而,按键的顺序读取记录需要一块接一块地全扫描索引叶子块。虽然快速全扫描比全索引扫描高校得多,但是快速全扫描无法按索引顺序返回记录,因此也不能用来避免排序操作。
SQL> select * from customers order by cust_last_name,cust_first_name,cust_year_of_birth;
55500 rows selected.
Execution Plan----------------------------------------------------------Plan hash value: 2792773903----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |----------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 55500 | 9810K| | 2609 (1)| 00:00:02 || 1 | SORT ORDER BY | | 55500 | 9810K| 12M| 2609 (1)| 00:00:02 || 2 | TABLE ACCESS FULL| CUSTOMERS | 55500 | 9810K| | 405 (1)| 00:00:01 |----------------------------------------------------------------------------------------Statistics---------------------------------------------------------- 12 recursive calls 15 db block gets 1456 consistent gets 2903 physical reads 0 redo size 6366362 bytes sent via SQL*Net to client 41213 bytes received via SQL*Net from client 3701 SQL*Net roundtrips to/from client 0 sorts (memory) 1 sorts (disk) 55500 rows processed建索引后:
SQL> create index cust_namedob_i on customers(cust_last_name,cust_first_name,cust_year_of_birth);
Index created.
SQL> select /*+ index(customers,cust_namedob_i) */ * from customers order by cust_last_name,cust_first_name,cust_year_of_birth;
55500 rows selected.
Execution Plan----------------------------------------------------------Plan hash value: 1819843466----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |----------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 55500 | 9810K| 20550 (1)| 00:00:15 || 1 | TABLE ACCESS BY INDEX ROWID| CUSTOMERS | 55500 | 9810K| 20550 (1)| 00:00:15 || 2 | INDEX FULL SCAN | CUST_NAMEDOB_I | 55500 | | 225 (0)| 00:00:01 |----------------------------------------------------------------------------------------------Statistics---------------------------------------------------------- 1 recursive calls 0 db block gets 26557 consistent gets 1708 physical reads 0 redo size 6366312 bytes sent via SQL*Net to client 41213 bytes received via SQL*Net from client 3701 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 55500 rows processed虽然使用索引可能就不再需要排序了,但是同时读取索引和表块,以及按块顺次读取这种并不高效的扫描方式所带来的开销,比使用全表扫描读取表块的方式要欠佳很多,通常,这意味为了避免排序而使用索引,实际上会导致更差的性能。然而使用索引在检索第一行记录时速度更快,因为一旦需要的记录被检索到,它会立即返回。相比之下排序的方法要求在任一记录返回之前,全部记录都必须被检索出来并完成排序。因此,在优化器目标为FIRST_ROWS_N时,优化器倾向于使用索引,而在目标是ALL_ROWS时,则会使用全表扫描。
另一个基于索引的获取比先扫描再获取要更优异的场景是当内存极其有限时。如果可用于排序的内存是受限的,读写临时段所需要IO将超过索引和和表扫描所包含的额外的IO开销。当然如果能够分配更多的内存,它的表现会好很多的,但是如果这是不可能的,你或许应该使用INDEX提示来避免排序。
聚合操作
聚合炒作(如SUM和AVG)必须处理输入的数据每一行记录,因此,它们通常和全表扫描联系在一起。
SQL> select sum(quantity_sold) from sales;
Execution Plan----------------------------------------------------------Plan hash value: 3519235612----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |----------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 3 | 525 (2)| 00:00:01 | | || 1 | SORT AGGREGATE | | 1 | 3 | | | | || 2 | PARTITION RANGE ALL| | 918K| 2691K| 525 (2)| 00:00:01 | 1 | 28 || 3 | TABLE ACCESS FULL | SALES | 918K| 2691K| 525 (2)| 00:00:01 | 1 | 28 |----------------------------------------------------------------------------------------------Statistics---------------------------------------------------------- 2429 recursive calls 2 db block gets 5371 consistent gets 1714 physical reads 0 redo size 538 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 183 sorts (memory) 0 sorts (disk) 1 rows processedSQL> select /*+ index(sales,index_sl) */ sum(quantity_sold) from sales;
SUM(QUANTITY_SOLD)
------------------ 918843 Execution Plan----------------------------------------------------------Plan hash value: 3788238680-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-----------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 3 | 2316 (1)| 00:00:02 || 1 | SORT AGGREGATE | | 1 | 3 | | || 2 | INDEX FULL SCAN| INDEX_SL | 918K| 2691K| 2316 (1)| 00:00:02 |----------------------------------------------------------------------------- Statistics---------------------------------------------------------- 1 recursive calls 0 db block gets 2311 consistent gets 2314 physical reads 0 redo size 538 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed最大值和最小值,
与大多数其他的聚合操作不同,如果在相关列存在索引,MAX和MIN操作并不需要读取每一行记录。如果存在B树索引,我们可以通过检查第一个或最后一个索引项来确定最大值或最小值,这仅需要3-5个逻辑读的开销:
SQL> select max(amount_sold) from sales;
MAX(AMOUNT_SOLD)
---------------- 1782.72 Execution Plan----------------------------------------------------------Plan hash value: 781264156----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |----------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 5 | 3 (0)| 00:00:01 || 1 | SORT AGGREGATE | | 1 | 5 | | || 2 | INDEX FULL SCAN (MIN/MAX)| AMOUNT_SOLD_IDX | 1 | 5 | 3 (0)| 00:00:01 |----------------------------------------------------------------------------------------------Statistics---------------------------------------------------------- 1 recursive calls 0 db block gets 3 consistent gets 8 physical reads 0 redo size 536 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed同时找出最大值和最小值,
SQL> select max(amount_sold),min(amount_sold) from sales;
MAX(AMOUNT_SOLD) MIN(AMOUNT_SOLD)
---------------- ---------------- 1782.72 6.4 Execution Plan----------------------------------------------------------Plan hash value: 3519235612----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |----------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 5 | 525 (2)| 00:00:01 | | || 1 | SORT AGGREGATE | | 1 | 5 | | | | || 2 | PARTITION RANGE ALL| | 918K| 4486K| 525 (2)| 00:00:01 | 1 | 28 || 3 | TABLE ACCESS FULL | SALES | 918K| 4486K| 525 (2)| 00:00:01 | 1 | 28 |----------------------------------------------------------------------------------------------Statistics---------------------------------------------------------- 1 recursive calls 0 db block gets 1635 consistent gets 1619 physical reads 0 redo size 618 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed实际上分别提交MAX和MIN查询然后将结果合并到一起是一种更好的方法:
SELECT max_sold, min_sold
FROM (SELECT MAX(amount_sold) max_sold FROM sales) maxt, 2 3 (SELECT MIN(amount_sold) min_sold FROM sales) mint;MAX_SOLD MIN_SOLD
---------- ---------- 1782.72 6.4 Execution Plan----------------------------------------------------------Plan hash value: 3650580342------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 26 | 6 (0)| 00:00:01 || 1 | NESTED LOOPS | | 1 | 26 | 6 (0)| 00:00:01 || 2 | VIEW | | 1 | 13 | 3 (0)| 00:00:01 || 3 | SORT AGGREGATE | | 1 | 5 | | || 4 | INDEX FULL SCAN (MIN/MAX)| AMOUNT_SOLD_IDX | 1 | 5 | 3 (0)| 00:00:01 || 5 | VIEW | | 1 | 13 | 3 (0)| 00:00:01 || 6 | SORT AGGREGATE | | 1 | 5 | | || 7 | INDEX FULL SCAN (MIN/MAX)| AMOUNT_SOLD_IDX | 1 | 5 | 3 (0)| 00:00:01 |------------------------------------------------------------------------------------------------ Statistics---------------------------------------------------------- 1 recursive calls 0 db block gets 6 consistent gets 5 physical reads 0 redo size 602 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed前N 查询
如何获取一个表的前10行记录,
错误写法:
SQL> SELECT * FROM sales WHERE rownum <= 10 ORDER BY amount_sold DESC;
PROD_ID CUST_ID TIME_ID CHANNEL_ID PROMO_ID QUANTITY_SOLD AMOUNT_SOLD
---------- ---------- ------------------- ---------- ---------- ------------- ----------- 13 987 1998-01-10 00:00:00 3 999 1 1232.16 13 1660 1998-01-10 00:00:00 3 999 1 1232.16 13 1762 1998-01-10 00:00:00 3 999 1 1232.16 13 1843 1998-01-10 00:00:00 3 999 1 1232.16 13 4663 1998-01-10 00:00:00 3 999 1 1232.16 13 2273 1998-01-10 00:00:00 3 999 1 1232.16 13 2380 1998-01-10 00:00:00 3 999 1 1232.16 13 2683 1998-01-10 00:00:00 3 999 1 1232.16 13 2865 1998-01-10 00:00:00 3 999 1 1232.16 13 1948 1998-01-10 00:00:00 3 999 1 1232.1610 rows selected.
这是因为对where的处理会先于order by。因此这个查询将获取它最先发现的10条记录,然后对它们进行排序。这样的结果不是真正的前10.
下面的查询更好:
SELECT /* top10_subquery */
* FROM (SELECT cust_id, prod_id, time_id, amount_sold FROM sales ORDER BY amount_sold DESC) WHERE rownum <= 10; 4 5 6 CUST_ID PROD_ID TIME_ID AMOUNT_SOLD---------- ---------- ------------------- ----------- 3948 18 1999-04-26 00:00:00 1782.72 4150 18 1999-06-26 00:00:00 1782.72 40 18 1999-06-26 00:00:00 1782.72 33724 18 1999-06-21 00:00:00 1782.72 32863 18 1999-06-21 00:00:00 1782.72 31364 18 1999-06-21 00:00:00 1782.72 10864 18 1999-06-21 00:00:00 1782.72 10620 18 1999-06-21 00:00:00 1782.72 6490 18 1999-06-21 00:00:00 1782.72 4788 18 1999-06-21 00:00:00 1782.7210 rows selected.