18. INDEX
INDEX 使用注意事项
• 并不是所有的情况下,使用索引访问都能加快速度。
• 索引的可选择性非常重要。当索引的可选择性比较高(具有相
同值的行很少),而我们需要数据量又比较小的时候,索引才能
极大的提高数据访问的速度。当索引的可选择性比较差的时候,
而我们使用的数据量又比较大,此时,使用索引反而会使查询的
速度变慢。
• 针对索引的列,需要匹配索引列的类型,也不能在其上使用计
算、函数等操作,否则,会使索引失效。
• 索引的维护需要成本,会带来 DML 的效率降低。所以只有在
需要的时候才能去创建索引。
19. INDEX
INDEX 使用注意事项
• 当一个表上存在多个索引,只能使用这个表上的一个索引。
( 具体使用哪一个索引由 Altibase 优化器决定 )
SELECT Y.ENAME
FROM EMPLOYEE X
WHERE
X.ENO = 8890 AND X.ENAME = ‘MSKIM’
ENO 上存在索引 IDX_EMP1 , ENAME 上存在索引 IDX_EMP2 ,当我们访问表
EMPLOYEE 的时候,优化器只会选择 IDX_EMP1, IDX_EMP2 中的一个进行访问。
20. INDEX
How Index Works (Using unique index)
SELECT * FROM EMPLOYEE WHERE ENO = 1;
ENO 是表的 PRIMARY KEY,
PRIMARY KEY 是非空唯一索引。
21. INDEX
How Index Works (Using unique index)
Query
Processor
__SYS_IDX_ID_403 EMPLOYEE
(INDEX) (TABLE)
Sorted Not Sorted
-索引是有序存放的,表中的数据是无序存放的。
-索引查询的时候首先会访问 B 树索引的根节点,再根据根节点找到
索引的叶子节点(叶子节点中存放直接指向表数据行的指针)。
-根据叶子节点的指针,直接读到表的数据行。
22. INDEX
How Index Works (Using non unique index)
SELECT * FROM EMPLOYEE WHERE DNO = 1003;
Department – Employee 是
1 对多的关系,所以 Employee 表的
DNO 字段会存在重复的数据。
23. INDEX
How Index Works (Using non unique index)
Query
Processor EMP_IDX1 EMPLOYEE
(INDEX) (TABLE)
Sorted Not Sorted
-假如查找 DNO 为 1003 的行。根据索引的叶子节点,会得到两个
指向到 EMPLOYEE 表的指针。
-根据这两个指针,就可以读取到 EMPLOYEE 表的两行数据。
24. INDEX
How Index Works (Index scan fail)
SELECT * FROM EMPLOYEE WHERE UPPER(DNO) = ‘1’;
对 ENO 列使用函数 UPPER 。
25. INDEX
How Index Works (Index scan fail)
Query
Processor
EMPLOYEE
(TABLE)
EMP_IDX1
(INDEX)
Not Sorted
-UPPER(DNO) 将导致索引访问失效。查询将使用全表扫描
方式访问。
-Altibase 不提供基于函数的索引 FBI(Function Based
Index) ,所以在这种情况下,只能使用全表扫描。
26. INDEX
INDEX SCAN Fail 的几种情况
• 对索引的列进行计算或者使用了函数。
Ex) SELECT * FROM T1 WHERE C1 +1 > 0
SELECT * FROM T1 WHERE TO_CHAR(SOME_DATE) = ‘2007-01-01’
• 索引列的类型不匹配
Ex) SELECT * FROM T1 WHERE CHAR_COLUMN = 1
Cf) CHAR 是 VARCHAR 类型的列。
• Composite Index 中没有使用索引的引导列。
Ex) C1 + C2 是 Composite Index
SELECT * FROM T1 WHERE C2 = :value
Cf ) 把语句改成下面的方式,就可以使用 Composite Index
SELECT * FROM T1 WHERE C1 = :value1 and C2 = :value2
• Altibase Optimizer 判断全表扫描的成本比索引扫描的成本
更低的时候,将不使用索引。
27. INDEX
INDEX SCAN Fail 的几种情况 ( 续 )
• NOT IN 子查询中的索引将失效
Ex) SELECT * FROM EMPLOYEE WHERE DNO NOT IN
(SELECT DNO FROM DEPARTMENT WHERE DNO > 4);
28. INDEX
即使 INDEX SCAN , Acess Count 依然很高
• 使用 Cardinality 不好的 Index 时发生
-> 确认使用哪个 Index
-> A+B+C 形式的组合 Index 时,如果以 A, C 为条件,则
Access Count 有可能高 . ( 因为只使用 A 条件 )
29. Join Optimization
The Cases of Join optimization
• JOIN CONDITION 中 FULL SCAN 或 Index Access Count 较
高时,需要考虑创建 Index 或变更 Index 顺序
• Index 使用效率不高或无法使用导致 FULL SCAN 时
• Altibase Optimizer 错误判断 JOIN Method 时
• 因过多表的 JOIN ,生成和验证执行计划的时间较长时
• 该 GROUPING – JOIN 的语句以 JOIN – GROUPING 顺序做
成时
31. Join Optimization
CASE2 (EMP_IDX INDEX 被删除 )
这个 CASE 描述的是非正常情况下,我们需要把全表扫描的结果集作为驱动表,
才能取得比较好的性能。
- EMPLOYEE 全表扫描,根据得到 DNO 再去 JOIN DEPARTMENT 表。
- 在 INDEX 被 DROP 的情况下,这是比较好的选择。如果这两个扫描的顺序反一下,
执行计划将变得比较糟糕,因为增加了 EMPLOYEE 表全表扫描的次数。
32. Join Optimization
Type mismatch in Join condition
• 建议 Join Condition 里使用的数据类型保持一致
• 内部进行型转换时,无法使用 INDEX
• 不得不使用的情况时确认 PLAN 并验证是否能使用 INDEX
33. Join Optimization
Specify your table only once if you possible.
• Inline View , SubQuery 虽然提供 SQL 的易用性和便利性,但有
一直访问同一张表的问题
• 尽量只写一次表名,减少同一张表的重复访问
• 优化成只访问一次表能获得结果集,如果不可行也要最少化访问
34. Join Optimization
Specify your table only once if you possible.
• 尽量减少表的访问次数。例如下面这种情况
SELECT * FROM T1
WHERE (T1. i1 = 1 AND T1.i2 IN (SELECT a2 FROM T2 WHERE T2.a1 = T1.i1))
OR (T1. i1 = 2 AND T1.i2 IN (SELECT a2 FROM T2 WHERE T2.a1 = T1.i1));‘
上面的语句应当改成:
SELECT * FROM T1
WHERE T1. i1 IN (1, 2)
AND T1.i2 IN (SELECT a2 FROM T2 WHERE T2.a1 = T1.i1));
35. Join Optimization
Reduce results before joining, if you possible.
• 尽可能的缩小结果集,然后再进行 join
例如 :
SELECT Y.DNO,
Y.DNAME,
X.TOTAL_SALARY
SELECT Y.DNO,
FROM
Y.DNAME,
(
SUM(SALARY) TOTAL_SALARY
SELECT DNO,
FROM EMPLOYEE X INNER JOIN
SUM(SALARY) TOTAL_SALARY
DEPARTMENT Y ON X.DNO = Y.DNO
FROM
GROUP BY
EMPLOYEE
Y.DNO, Y.DNAME
GROUP BY DNO
) X INNER JOIN DEPARTMENT Y
ON X.DNO = Y.DNO
36. Join Optimization
Outer Join Optimization
• Outer Join 时, Key Access Path 为基准表。
SELECT X.DNAME, Y.ENAME
FROM DEPARTMENT X LEFT OUTER JOIN
EMPLOYEE Y ON X.DNO = Y.DNO
WHERE
X.DNO = 1000 AND Y.ENO = 8890
上面的示例中, X 表会作为基准表, X.DNO = 1000 会走索引。
Y 表则根据连接条件 X.DNO = Y.DNO 进行关联,不会使用 Y 表上的 Y.ENO=8890 上的索引。
37. Join Optimization
Outer Join Optimization
• 基准表没有索引时?
SELECT X.DNAME, Y.ENAME SELECT X.DNAME, Y.ENAME
FROM DEPARTMENT X LEFT OUTER JOIN FROM DEPARTMENT X INNER JOIN
EMPLOYEE Y ON X.DNO = Y.DNO EMPLOYEE Y ON X.DNO = Y.DNO
WHERE WHERE
Y.ENO = 8890 Y.ENO = 8890
以上两个查询的结果是一致的。
对于 OUTER JOIN , Y.ENO 即使为 PK ,也无法使用索引。
而对于 INNER JOIN ,则可以使用索引。
不必要的 OUTER JOIN 应当避免,可以使用 INNER JOIN 来代替。
38. Limit Optimization
Question
想在 EMPLOYEE 表中找到 SALARY 的前两名。
将 SALARY 排序取得前两名。
SELECT ENAME, SALARY
FROM EMPLOYEE
ORDER BY SALARY DESC
LIMIT 2
结果是对的,但是如果这个语句执行的频率非常高呢 ?
42. Limit Optimization
Limit Stop key 使用说明
• 一般的情况下,我们使用 limit 的目的是为了减少表的访问量。
• 下面的几种情况下 limit 将无法有效发挥作用:
1 、 GROUP BY 将使 limit
2 、无法使用索引的 order by 操作
3 、带很多的 where 条件
43. SUB-QUERY
The Cases of Subquery Optimization
• 因使用过多的 Subquery ,频繁访问同一张表时
• 因对 NOT IN 的理解不够,对于大的结果集使用 NOT IN 时
• Subquery 条件里没有 INDEX 字段时
44. SUB-QUERY
If you read your table twice or more …
• Subquery 是嵌入到另一个 SQL 语句中的 SELECT 语句,频繁读取
同一张表时影响性能
• 内查询的结果集被用作外查询的搜索值,所以内查询的结果集越大性
能下降越严重 ( 注意 !!)
• 如果 SubQuery 的结果集里只取一条记录,建议在 SubQuery
里使用 LIMIT 1
• 如果同一张表被查询两次以上,尽量把 SubQuery 转换为 JOIN
• 所有的 SubQuery 都可以转换为 Join 形态
45. SUB-QUERY
SubQuery -> Joining form
• 使用表连接取代子查询。一般情况下,表连接的效率高于子查询。
SELECT
X.ENAME,
(SELECT DNAME FROM DEPARTMENT WHERE DNO = X.DNO) DNAME,
(SELECT DTEL FROM DEPARTMENT WHERE DNO = X.DNO) DTEL ,
(SELECT DLOCATION FROM DEPARTMENT WHERE DNO = X.DNO) DLOCATION
FROM
EMPLOYEE X;
SELECT
X.ENAME,
Y.DNAME,
Y.DTEL ,
Y.DLOCATION
FROM
EMPLOYEE X INNER JOIN DEPARTMENT Y ON X.DNO = Y.DNO;
46. SUB-QUERY
Avoiding NOT IN Sub Query …
• NOT IN 无法使用索引。
• 使用 JOIN 取代 NOT IN 。
SELECT * FROM EMPLOYEE WHERE DNO NOT IN (
SELECT DNO FROM DEPARTMENT_DELETED);
SELECT X.*
FROM EMPLOYEE X LEFT OUTER JOIN
DEPARTMENT_DELETED Y
ON X.DNO = Y.DNO
WHERE Y.DNO IS NULL