背景:

一张2000万的数据表和一张50万的数据表关联查询,关联的条件是=号和>号,还有一些其他等值条件,查询时间要15秒,关键这条语句要查询好多次,在SQL ordered by Reads、SQL ordered by User I/O Wait Time、SQL ordered by Elapsed Time中都能看到这条语句的身影,所以必须要优化。

在家里模拟了相关过程

第一步创建模拟表

第二步分析数据分布状况

第三步不加索引看执行计划

第四步保持原有索引看执行计划

第五步改变为组合索引看执行计划

最后结论是走组合索引方式。

代码示例

--要执行的SQL语句

  1. SELECT count(*) from (
  2. SELECT a.tid,max(b.sysncdate)
  3. FROM maintable a,othertable b
  4. WHERE a.tid=b.logid
  5. AND a.sysncdate>b.sysncdate
  6. AND b.randomi=1
  7. AND a.owner='SYS'
  8. GROUP BY a.tid);

--创建模拟表

  1. CREATE TABLE maintable as

  2. SELECT a.*,

  3. DBMS_RANDOM.STRING('l',4) tid,

  4. SYSDATE-TRUNC(DBMS_RANDOM.VALUE(1,10001))/24/60 sysncdate

  5. FROM dba_tables a,

  6. (

  7. SELECT level,ROWNUM rn

  8. FROM DUAL

  9. CONNECT BY ROWNUM<=3000

  10. );

  11. CREATE TABLE othertable as

  12. SELECT TRUNC(DBMS_RANDOM.VALUE(1,101)) as randomi,

  13. DBMS_RANDOM.string('~',15) as random_,

  14. DBMS_RANDOM.string('l',15) as randoml,

  15. DBMS_RANDOM.string('a',15) as randoma,

  16. DBMS_RANDOM.string('A',15) as randomuppera,

  17. DBMS_RANDOM.string('u',15) as randomu,

  18. DBMS_RANDOM.string('U',15) as randomupperu,

  19. DBMS_RANDOM.string('x',15) as randomx,

  20. DBMS_RANDOM.string('X',15) as randomupperx,

  21. DBMS_RANDOM.string('p',15) as randomp,

  22. DBMS_RANDOM.string('P',15) as randomupperp,

  23. a.tid as logid,

  24. SYSDATE-TRUNC(DBMS_RANDOM.VALUE(1,10001))/24/60 sysncdate

  25. FROM (SELECT DISTINCT tid FROM maintable ) a

--分析表和列的分布

  1. SELECT A.owner,a.table_name,a.num_rows/1024/1024,a.blocks,a.avg_row_len,a.last_analyzed

  2. FROM DBA_TABLES A

  3. WHERE A.TABLE_NAME IN ('MAINTABLE','OTHERTABLE');

  4. SELECT A.owner,a.segment_name,a.segment_type,a. bytes/1024/1024,a.blocks

  5. FROM DBA_SEGMENTS A

  6. WHERE A.SEGMENT_NAME IN ('MAINTABLE','OTHERTABLE');

  7. SELECT A.owner,a.table_name,a.column_name,a.data_type,a.num_distinct,a.densitya.num_nulls

  8. FROM DBA_TAB_COLUMNS A

  9. WHERE A.TABLE_NAME IN ('MAINTABLE','OTHERTABLE');

  10. SELECT a.owner,a.index_name,a.index_type,a.table_name,a.clustering_factor,a.num_rows,a.degree,a.last_analyzed

  11. FROM DBA_INDEXES A

  12. WHERE A.TABLE_NAME IN ('MAINTABLE','OTHERTABLE');

  13. Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.1.0

  14. Connected as usera@ORCL

  15. SQL>

  16. SQL> SELECT A.owner,a.table_name,a.num_rows/1024/1024,a.blocks,a.avg_row_len,a.last_analyzed

  17. 2 FROM DBA_TABLES A

  18. 3 WHERE A.TABLE_NAME IN ('MAINTABLE','OTHERTABLE');

  19. OWNER TABLE_NAME A.NUM_ROWS/1024/1024 BLOCKS AVG_ROW_LEN LAST_ANALYZED

  20. ------------------------------ ------------------------------ -------------------- ---------- ----------- -------------

  21. USERA MAINTABLE 8.11150646209717 311289 259 2020-11-22 0:

  22. USERA OTHERTABLE 0.441422462463379 11869 179 2020-11-22 0:

  23. SQL> SELECT A.owner,a.segment_name,a.segment_type,a. bytes/1024/1024,a.blocks

  24. 2 FROM DBA_SEGMENTS A

  25. 3 WHERE A.SEGMENT_NAME IN ('MAINTABLE','OTHERTABLE');

  26. OWNER SEGMENT_NAME SEGMENT_TYPE A.BYTES/1024/1024 BLOCKS

  27. ------------------------------ -------------------------------------------------------------------------------- ------------------ ----------------- ----------

  28. USERA OTHERTABLE TABLE 96 12288

  29. USERA MAINTABLE TABLE 2432 311296

  30. SQL> SELECT A.owner,a.table_name,a.column_name,a.data_type,a.num_distinct,a.densitya.num_nulls

  31. 2 FROM DBA_TAB_COLUMNS A

  32. 3 WHERE A.TABLE_NAME IN ('MAINTABLE','OTHERTABLE');

  33. OWNER TABLE_NAME COLUMN_NAME DATA_TYPE NUM_DISTINCT DENSITY NUM_NULLS

  34. ------------------------------ ------------------------------ ------------------------------ -------------------------------------------------------------------------------- ------------ ---------- ----------

  35. USERA OTHERTABLE SYSNCDATE DATE 10016 9.98402555 0

  36. USERA OTHERTABLE LOGID VARCHAR2 462865 2.16045715 0

  37. USERA OTHERTABLE RANDOMUPPERP VARCHAR2 462865 2.16045715 0

  38. USERA OTHERTABLE RANDOMP VARCHAR2 462865 2.16045715 0

  39. USERA OTHERTABLE RANDOMUPPERX VARCHAR2 462865 2.16045715 0

  40. USERA OTHERTABLE RANDOMX VARCHAR2 462865 2.16045715 0

  41. USERA OTHERTABLE RANDOMUPPERU VARCHAR2 462865 2.16045715 0

  42. USERA OTHERTABLE RANDOMU VARCHAR2 462865 2.16045715 0

  43. USERA OTHERTABLE RANDOMUPPERA VARCHAR2 462865 2.16045715 0

  44. USERA OTHERTABLE RANDOMA VARCHAR2 462865 2.16045715 0

  45. USERA OTHERTABLE RANDOML VARCHAR2 462865 2.16045715 0

  46. USERA OTHERTABLE RANDOM_ VARCHAR2 462865 2.16045715 0

  47. USERA OTHERTABLE RANDOMI NUMBER 100 0.01 0

  48. USERA MAINTABLE SYSNCDATE DATE 10000 0.0001 0

  49. USERA MAINTABLE TID VARCHAR2 439709 2.27423136 0

  50. USERA MAINTABLE RESULT_CACHE VARCHAR2 1 1 0

  51. USERA MAINTABLE SEGMENT_CREATED VARCHAR2 3 0.33333333 0

  52. USERA MAINTABLE READ_ONLY VARCHAR2 1 1 0

  53. USERA MAINTABLE DROPPED VARCHAR2 1 1 0

  54. USERA MAINTABLE COMPRESS_FOR VARCHAR2 2 0.5 7673823

  55. USERA MAINTABLE COMPRESSION VARCHAR2 2 0.5 192533

  56. USERA MAINTABLE DEPENDENCIES VARCHAR2 1 1 0

  57. USERA MAINTABLE CLUSTER_OWNER VARCHAR2 1 1 8393696

  58. USERA MAINTABLE MONITORING VARCHAR2 2 0.5 0

  59. USERA MAINTABLE SKIP_CORRUPT VARCHAR2 1 1 0

  60. USERA MAINTABLE DURATION VARCHAR2 2 0.5 8171771

  61. USERA MAINTABLE USER_STATS VARCHAR2 1 1 0

  62. USERA MAINTABLE GLOBAL_STATS VARCHAR2 2 0.5 0

  63. USERA MAINTABLE ROW_MOVEMENT VARCHAR2 2 0.5 0

  64. USERA MAINTABLE CELL_FLASH_CACHE VARCHAR2 1 1 0

  65. USERA MAINTABLE FLASH_CACHE VARCHAR2 1 1 0

  66. USERA MAINTABLE BUFFER_POOL VARCHAR2 1 1 0

  67. USERA MAINTABLE NESTED VARCHAR2 2 0.5 0

  68. USERA MAINTABLE SECONDARY VARCHAR2 2 0.5 0

  69. USERA MAINTABLE TEMPORARY VARCHAR2 2 0.5 0

  70. USERA MAINTABLE IOT_TYPE VARCHAR2 2 0.5 7870239

  71. USERA MAINTABLE PARTITIONED VARCHAR2 2 0.5 0

  72. USERA MAINTABLE LAST_ANALYZED DATE 539 0.00185528 417582

  73. USERA MAINTABLE SAMPLE_SIZE NUMBER 369 0.00271002 417582

  74. USERA MAINTABLE TABLE_LOCK VARCHAR2 1 1 0

  75. USERA MAINTABLE CACHE VARCHAR2 1 1 0

  76. USERA MAINTABLE INSTANCES VARCHAR2 2 0.5 0

  77. USERA MAINTABLE DEGREE VARCHAR2 1 1 0

  78. USERA MAINTABLE NUM_FREELIST_BLOCKS NUMBER 1 1 896846

  79. USERA MAINTABLE AVG_SPACE_FREELIST_BLOCKS NUMBER 1 1 417582

  80. USERA MAINTABLE AVG_ROW_LEN NUMBER 236 0.00423728 417582

  81. USERA MAINTABLE CHAIN_CNT NUMBER 1 1 417582

  82. USERA MAINTABLE AVG_SPACE NUMBER 6 0.16666666 417582

  83. USERA MAINTABLE EMPTY_BLOCKS NUMBER 6 0.16666666 896846

  84. USERA MAINTABLE BLOCKS NUMBER 94 0.01063829 896846

  85. USERA MAINTABLE NUM_ROWS NUMBER 378 0.00264550 417582

  86. USERA MAINTABLE BACKED_UP VARCHAR2 1 1 0

  87. USERA MAINTABLE LOGGING VARCHAR2 2 0.5 678189

  88. USERA MAINTABLE FREELIST_GROUPS NUMBER 1 1 6237550

  89. USERA MAINTABLE FREELISTS NUMBER 1 1 6237550

  90. USERA MAINTABLE PCT_INCREASE NUMBER 0 1.17570554 8505531

  91. USERA MAINTABLE MAX_EXTENTS NUMBER 1 1 3531023

  92. USERA MAINTABLE MIN_EXTENTS NUMBER 1 1 3531023

  93. USERA MAINTABLE NEXT_EXTENT NUMBER 6 0.16666666 3515751

  94. USERA MAINTABLE INITIAL_EXTENT NUMBER 18 0.05555555 3515751

  95. USERA MAINTABLE MAX_TRANS NUMBER 2 0.5 192533

  96. USERA MAINTABLE INI_TRANS NUMBER 6 0.16666666 192533

  97. USERA MAINTABLE PCT_USED NUMBER 3 0.33333333 5748851

  98. USERA MAINTABLE PCT_FREE NUMBER 6 0.16666666 192533

  99. USERA MAINTABLE STATUS VARCHAR2 1 1 0

  100. USERA MAINTABLE IOT_NAME VARCHAR2 50 0.02 8355896

  101. USERA MAINTABLE CLUSTER_NAME VARCHAR2 10 0.1 8393696

  102. USERA MAINTABLE TABLESPACE_NAME VARCHAR2 9 0.11111111 1014992

  103. USERA MAINTABLE TABLE_NAME VARCHAR2 2812 0.00035561 0

  104. USERA MAINTABLE OWNER VARCHAR2 30 0.03333333 0

  105. 70 rows selected

  106. SQL> SELECT a.owner,a.index_name,a.index_type,a.table_name,a.clustering_factor,a.num_rows,a.degree,a.last_analyzed

  107. 2 FROM DBA_INDEXES A

  108. 3 WHERE A.TABLE_NAME IN ('MAINTABLE','OTHERTABLE');

  109. OWNER INDEX_NAME INDEX_TYPE TABLE_NAME CLUSTERING_FACTOR NUM_ROWS DEGREE LAST_ANALYZED

  110. ------------------------------ ------------------------------ --------------------------- ------------------------------ ----------------- ---------- ---------------------------------------- -------------

  111. SQL> EXPLAIN PLAN FOR

  112. 2 SELECT count(*) from (

  113. 3 SELECT a.tid,max(b.sysncdate)

  114. 4 FROM maintable a,othertable b

  115. 5 WHERE a.tid=b.logid

  116. 6 AND a.sysncdate>b.sysncdate

  117. 7 AND b.randomi=1

  118. 8 AND a.owner='SYS'

  119. 9 GROUP BY a.tid);

  120. Explained

  121. SQL> SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE'));

  122. PLAN_TABLE_OUTPUT

  123. --------------------------------------------------------------------------------

  124. Plan hash value: 2942004947

  125. --------------------------------------------------------------------------------

  126. | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time

  127. --------------------------------------------------------------------------------

  128. | 0 | SELECT STATEMENT | | 1 | | 87839 (1)| 00:17:

  129. | 1 | SORT AGGREGATE | | 1 | | |

  130. | 2 | VIEW | | 3085 | | 87839 (1)| 00:17:

  131. | 3 | HASH GROUP BY | | 3085 | 92550 | 87839 (1)| 00:17:

  132. |* 4 | HASH JOIN | | 3085 | 92550 | 87838 (1)| 00:17:

  133. |* 5 | TABLE ACCESS FULL| OTHERTABLE | 4629 | 60177 | 3229 (1)| 00:00:

  134. |* 6 | TABLE ACCESS FULL| MAINTABLE | 283K| 4706K| 84607 (1)| 00:16:

  135. --------------------------------------------------------------------------------

  136. Predicate Information (identified by operation id):

  137. ---------------------------------------------------

  138. 4 - access("A"."TID"="B"."LOGID")

  139. filter("A"."SYSNCDATE">"B"."SYSNCDATE")

  140. 5 - filter("B"."RANDOMI"=1)

  141. PLAN_TABLE_OUTPUT

  142. --------------------------------------------------------------------------------

  143. 6 - filter("A"."OWNER"='SYS')

  144. 21 rows selected

  145. SQL> SQL>

  146. SQL> create index maintable_index_tid on maintable (tid);

  147. Index created

  148. SQL> create index maintable_index_sysncdate on maintable (sysncdate);

  149. Index created

  150. SQL> create index othertable_index_logid on othertable (logid);

  151. Index created

  152. SQL> create index othertable_index_sysncdate on othertable (sysncdate);

  153. Index created

  154. SQL> analyze table maintable estimate statistics sample 5 percent;

  155. Table analyzed

  156. SQL> analyze table othertable estimate statistics sample 5 percent;

  157. Table analyzed

  158. SQL> EXPLAIN PLAN FOR

  159. 2 SELECT count(*) from (

  160. 3 SELECT a.tid,max(b.sysncdate)

  161. 4 FROM maintable a,othertable b

  162. 5 WHERE a.tid=b.logid

  163. 6 AND a.sysncdate>b.sysncdate

  164. 7 AND b.randomi=1

  165. 8 AND a.owner='SYS'

  166. 9 GROUP BY a.tid);

  167. Explained

  168. SQL> SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE'));

  169. PLAN_TABLE_OUTPUT

  170. --------------------------------------------------------------------------------

  171. Plan hash value: 2942004947

  172. --------------------------------------------------------------------------------

  173. | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time

  174. --------------------------------------------------------------------------------

  175. | 0 | SELECT STATEMENT | | 1 | | 88495 (2)| 00:17:

  176. | 1 | SORT AGGREGATE | | 1 | | |

  177. | 2 | VIEW | | 3085 | | 88495 (2)| 00:17:

  178. | 3 | HASH GROUP BY | | 3085 | 92550 | 88495 (2)| 00:17:

  179. |* 4 | HASH JOIN | | 3085 | 92550 | 88494 (2)| 00:17:

  180. |* 5 | TABLE ACCESS FULL| OTHERTABLE | 4629 | 60177 | 3236 (1)| 00:00:

  181. |* 6 | TABLE ACCESS FULL| MAINTABLE | 283K| 4706K| 85255 (2)| 00:17:

  182. --------------------------------------------------------------------------------

  183. Predicate Information (identified by operation id):

  184. ---------------------------------------------------

  185. 4 - access("A"."TID"="B"."LOGID")

  186. filter("A"."SYSNCDATE">"B"."SYSNCDATE")

  187. 5 - filter("B"."RANDOMI"=1)

  188. PLAN_TABLE_OUTPUT

  189. --------------------------------------------------------------------------------

  190. 6 - filter("A"."OWNER"='SYS')

  191. 21 rows selected

  192. SQL> EXPLAIN PLAN FOR

  193. 2 select count(*) from (

  194. 3 select /*+INDEX(a maintable_index_tid) INDEX(b othertable_index_logid)*/ a.tid,max(b.sysncdate)

  195. 4 from maintable a,othertable b

  196. 5 where a.tid=b.logid

  197. 6 and a.sysncdate>b.sysncdate

  198. 7 and b.randomi=1

  199. 8 and a.owner='SYS'

  200. 9 group by a.tid);

  201. Explained

  202. SQL> SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE'));

  203. PLAN_TABLE_OUTPUT

  204. --------------------------------------------------------------------------------

  205. Plan hash value: 1035171181

  206. --------------------------------------------------------------------------------

  207. | Id | Operation | Name | Rows | Byte

  208. --------------------------------------------------------------------------------

  209. | 0 | SELECT STATEMENT | | 1 |

  210. | 1 | SORT AGGREGATE | | 1 |

  211. | 2 | VIEW | | 3085 |

  212. | 3 | SORT GROUP BY NOSORT | | 3085 | 9255

  213. | 4 | NESTED LOOPS | | |

  214. | 5 | NESTED LOOPS | | 3085 | 9255

  215. |* 6 | TABLE ACCESS BY INDEX ROWID| OTHERTABLE | 4629 | 6017

  216. | 7 | INDEX FULL SCAN | OTHERTABLE_INDEX_LOGID | 469K|

  217. |* 8 | INDEX RANGE SCAN | MAINTABLE_INDEX_TID | 19 |

  218. |* 9 | TABLE ACCESS BY INDEX ROWID | MAINTABLE | 1 | 1

  219. --------------------------------------------------------------------------------

  220. Predicate Information (identified by operation id):

  221. ---------------------------------------------------

  222. PLAN_TABLE_OUTPUT

  223. --------------------------------------------------------------------------------

  224. 6 - filter("B"."RANDOMI"=1)

  225. 8 - access("A"."TID"="B"."LOGID")

  226. 9 - filter("A"."OWNER"='SYS' AND "A"."SYSNCDATE">"B"."SYSNCDATE")

  227. 23 rows selected

  228. SQL> drop index maintable_index_tid;

  229. Index dropped

  230. SQL> drop index maintable_index_sysncdate;

  231. Index dropped

  232. SQL> drop index othertable_index_sysncdate;

  233. Index dropped

  234. SQL> drop index othertable_index_logid;

  235. Index dropped

  236. SQL> create index maintable_index on maintable (tid,sysncdate);

  237. Index created

  238. SQL> create index othertable_index on othertable (logid,sysncdate);

  239. Index created

  240. SQL> analyze table maintable estimate statistics sample 5 percent;

  241. Table analyzed

  242. SQL> analyze table othertable estimate statistics sample 5 percent;

  243. Table analyzed

  244. SQL>

  245. SQL> explain plan for

  246. 2 select count(*) from (

  247. 3 select a.tid,max(b.sysncdate)

  248. 4 from maintable a,othertable b

  249. 5 where a.tid=b.logid

  250. 6 and a.sysncdate>b.sysncdate

  251. 7 and b.randomi=1

  252. 8 and a.owner='SYS'

  253. 9 group by a.tid);

  254. Explained

  255. SQL> SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE'));

  256. PLAN_TABLE_OUTPUT

  257. --------------------------------------------------------------------------------

  258. Plan hash value: 2209219092

  259. --------------------------------------------------------------------------------

  260. | Id | Operation | Name | Rows | Bytes | Cost

  261. --------------------------------------------------------------------------------

  262. | 0 | SELECT STATEMENT | | 1 | | 5650

  263. | 1 | SORT AGGREGATE | | 1 | |

  264. | 2 | VIEW | | 3085 | | 5650

  265. | 3 | HASH GROUP BY | | 3085 | 92550 | 5650

  266. | 4 | NESTED LOOPS | | | |

  267. | 5 | NESTED LOOPS | | 3085 | 92550 | 5650

  268. |* 6 | TABLE ACCESS FULL | OTHERTABLE | 4629 | 60177 | 323

  269. |* 7 | INDEX RANGE SCAN | MAINTABLE_INDEX | 10 | |

  270. |* 8 | TABLE ACCESS BY INDEX ROWID| MAINTABLE | 1 | 17 | 1

  271. --------------------------------------------------------------------------------

  272. Predicate Information (identified by operation id):

  273. ---------------------------------------------------

  274. 6 - filter("B"."RANDOMI"=1)

  275. PLAN_TABLE_OUTPUT

  276. --------------------------------------------------------------------------------

  277. 7 - access("A"."TID"="B"."LOGID" AND "A"."SYSNCDATE">"B"."SYSNCDATE" AND

  278. "A"."SYSNCDATE" IS NOT NULL)

  279. 8 - filter("A"."OWNER"='SYS')

  280. 23 rows selected

  281. SQL> explain plan for

  282. 2 select count(*) from (

  283. 3 select /*+INDEX(a maintable_index) INDEX(b othertable_index)*/ a.tid,max(b.sysncdate)

  284. 4 from maintable a,othertable b

  285. 5 where a.tid=b.logid

  286. 6 and a.sysncdate>b.sysncdate

  287. 7 and b.randomi=1

  288. 8 and a.owner='SYS'

  289. 9 group by a.tid);

  290. Explained

  291. SQL> SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE'));

  292. PLAN_TABLE_OUTPUT

  293. --------------------------------------------------------------------------------

  294. Plan hash value: 3283341491

  295. --------------------------------------------------------------------------------

  296. | Id | Operation | Name | Rows | Bytes | Co

  297. --------------------------------------------------------------------------------

  298. | 0 | SELECT STATEMENT | | 1 | |

  299. | 1 | SORT AGGREGATE | | 1 | |

  300. | 2 | VIEW | | 3085 | |

  301. | 3 | SORT GROUP BY NOSORT | | 3085 | 92550 |

  302. | 4 | NESTED LOOPS | | | |

  303. | 5 | NESTED LOOPS | | 3085 | 92550 |

  304. |* 6 | TABLE ACCESS BY INDEX ROWID| OTHERTABLE | 4629 | 60177 |

  305. | 7 | INDEX FULL SCAN | OTHERTABLE_INDEX | 464K| | 1

  306. |* 8 | INDEX RANGE SCAN | MAINTABLE_INDEX | 10 | |

  307. |* 9 | TABLE ACCESS BY INDEX ROWID | MAINTABLE | 1 | 17 |

  308. --------------------------------------------------------------------------------

  309. Predicate Information (identified by operation id):

  310. ---------------------------------------------------

  311. PLAN_TABLE_OUTPUT

  312. --------------------------------------------------------------------------------

  313. 6 - filter("B"."RANDOMI"=1)

  314. 8 - access("A"."TID"="B"."LOGID" AND "A"."SYSNCDATE">"B"."SYSNCDATE" AND "A".

  315. IS NOT NULL)

  316. 9 - filter("A"."OWNER"='SYS')

  317. 24 rows selected

  318. SQL>