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”?

Oracle中Inner join和Where的区别

SQL UPDATE with INNER JOIN

Oracle中Inner join和Where的区别

(', CROSS, FULL, INNER, JOIN, LEFT, NATURAL, ON, RIGHT 或 USING 预期,得到'WITH'

INNER JOIN 之前的 WHERE 子句