【发布时间】:2016-04-02 12:45:46
【问题描述】:
假设我们有以下查询:
select name
from friends
where upper(name) like UPPER('%ESC%') and age = 20;
索引它的最佳方法是什么?
CREATE INDEX fr ON friends (AGE,UPPER(NAME));CREATE INDEX fr ON friends (AGE);
谢谢。
【问题讨论】:
假设我们有以下查询:
select name
from friends
where upper(name) like UPPER('%ESC%') and age = 20;
索引它的最佳方法是什么?
CREATE INDEX fr ON friends (AGE,UPPER(NAME));CREATE INDEX fr ON friends (AGE);谢谢。
【问题讨论】:
最好的方法是什么?两者几乎是等价的,尽管第一个对于查询的优势可能很小。
在较高级别上,您只能索引age,因为like 模式以通配符开头。因此,当age 是索引的第一列时,Oracle 可以使用该索引来查找所有年龄合适的人。
一个小警告。如果您有第一个索引,Oracle 可能足够聪明,不会应用 upper(name) 函数,因为该值已经在索引中。 Oracle 仍需要扫描索引中的所有条目以查找age = 20,但它不需要转到where 子句的数据页。这可能是一种节省——但通常不会是一个很大的节省。
如果查询是:
where upper(name) between 'ESC' and 'FSC' and age = 20;
那么第一个索引会更好,因为Oracle可以直接在索引中查找适当的行。
【讨论】:
可能这两个索引都无法帮助您完成此查询。
AGE 似乎很有选择性。只有一百多个不同的值(假设我们可以排除树木和建筑物作为朋友)。因此,似乎对 AGE 的搜索正在寻找所有可能记录的约 1%。但是,AGE 分布并不均匀:AGE=20 上的过滤器返回的记录可能比 AGE=99 多得多。
至于 UPPER(NAME),您对 like UPPER('%ESC%') 进行了限制。 CESC 将与 ESCOBAR 和 FRANCESCA 匹配。因此查询必须评估AGE=20 所在的每个名称。如果您在friends (AGE,UPPER(NAME)) 上有一个索引,则可以使用索引范围扫描评估整个 WHERE 子句,这将是相当有效的。 AGE 上的索引此时会导致表读取。
无论哪种方式,查询都需要读取表来获取整个记录,因为您要返回NAME 而不是UPPER(NAME)。如果您在AGE=20 上获得很多点击,因为一个通用名称元素会包含很多索引读取,这很昂贵。除非 FRIENDS 是一个非常宽的表(很多列),否则读取整个表的全表扫描可能会更有效。
性能调优是一个权衡问题。如果您在 FRIENDS 中有很多记录并且年龄分布很好并且您想在 AGE 和 UPPER(NAME) 上查询很多,那么 friends (AGE,UPPER(NAME)) 上的索引可能值得维护开销它。但是,如果它是一张小桌子或者你所有的朋友都是大学年龄,那么你很可能不应该为任何索引而烦恼。
简而言之,这里有很多变数:您所能做的就是对各种选项进行基准测试,看看哪些最适合您。
【讨论】: