INNER JOIN with WHERE lower(...) like ... 性能
Posted
技术标签:
【中文标题】INNER JOIN with WHERE lower(...) like ... 性能【英文标题】:INNER JOIN with WHERE lower(...) like ... performance 【发布时间】:2018-03-28 16:03:12 【问题描述】:我将查找所有作者姓名为“de%”的书籍不区分大小写。
我写:
SELECT * FROM authors a
INNER JOIN books b ON books.author_id = a.id
WHERE lower(a.first_name) like 'de%'
这会导致书籍的 FULL TABLE ACCESS,基数为 2037700,成本为 4342。
和简单一模一样
SELECT * FROM books; -- Same 2037700 cardinality and 4342 cost
如何告诉 oracle 按作者 ID 过滤书籍?当然,我有 books.author_id 的索引。
我能达到的最接近的结果是:
SELECT /*+ index(b) */ * FROM books b WHERE author_id IN (SELECT id FROM authors a WHERE lower(a.first_name) like 'de%');
--gives 1028759 cardinality and 47282 cost - still not so good
更新
是的,我有两个关于 authors.first_name 的索引:
CREATE INDEX first_name_idx ON authors (first_name);
CREATE INDEX first_name_lower_idx ON authors (lower(first_name));
执行计划表明 使用了 first_name_lower_idx,但基数等于 FULL SCAN authors。
UPD2
是的,不使用小写字母表示性能要好得多。
UPD3
子字符串并没有让任何事情变得更好。
区分大小写
功能索引不区分大小写
不区分大小写,无索引
【问题讨论】:
你有索引authors.first_name
吗?
这些天我不了解 Oracle,但较低的功能可能会导致索引被跳过。
我怀疑优化器会首先过滤 a.first_name。并使用不区分大小写的排序规则而不是更低的排序规则。
很好奇 WHERE substr(lower(a.first_name),0,2) = 'de' 是否会更好地使用索引。
更高版本的 oracle 允许您创建基于函数的索引——类似于 create index i1 on books(lower(first_name));然后它将使用该索引。是的,否则如果您对列执行函数,它将不会使用索引。此外,如果您使用通配符开头,它将不会使用索引,所以像 'de%' 将使用索引但 '%de%' 不会
【参考方案1】:
如果不使用索引,您的查询会好得多。此外,您似乎缺少连接。解释如下。
根据统计报告的基数,我假设Authors
表有10431条记录,Books
表有2037700条记录。
区分大小写的谓词WHERE cp.first_name like 'de%'
仅从Authors
表中获取10431 条记录中的13 条记录。随后与Books
表的连接仅从Books
表中获取2037700 条记录中的99 行。由于缺少一个或多个连接,您将获得 (13 * 99) 1257 行。尽管如此,这里还是倾向于使用索引,因为计数可以忽略不计。
不区分大小写的谓词WHERE lower(cp.first_name) like 'de%'
将“DE%”、“dE%”和“De%”名称也转换为“de%”名称。因此,在执行计划的第一步中,将从Authors
表中获取更多记录。根据执行计划的基数估计,不区分大小写的谓词从Authors
表中获取所有记录(10431 条记录)。随后与Book
表的连接过滤成1028912 条记录的最终结果集。如果您甚至使用提示强制在Books.author_id
上建立索引,您将命中所有(10431)author_ids
的索引。在这种情况下,索引只会是开销,而不是性能提升。
索引仅在以下情况下有用。
一个。当你取一张大桌子的一小部分时。 (索引唯一扫描或索引范围扫描) 湾。当查询中引用的所有列都是索引的一部分时。 (索引快速全扫描)。 C。当前导索引列具有低基数并且过滤器导致扫描最小数量的子树时。 (索引跳过扫描)。
另外请检查 Books.authors_id 是否有引用 Authors.id 的外键。
【讨论】:
以上是关于INNER JOIN with WHERE lower(...) like ... 性能的主要内容,如果未能解决你的问题,请参考以下文章
“INNER JOIN 之前的 Sub SELECT”还是“INNER JOIN 之后的 WHERE”?
(', CROSS, FULL, INNER, JOIN, LEFT, NATURAL, ON, RIGHT 或 USING 预期,得到'WITH'