我正在数据库书中阅读有关索引的内容,我想知道我的假设是否正确,即包含非常量表达式的 WHERE
子句不会使用索引。
所以如果我有
SELECT * FROM statuses WHERE app_user_id % 10 = 0;
这不会使用在 app_user_id 上创建的索引。但是
SELECT * FROM statuses WHERE app_user_id = 5;
将使用 app_user_id 上的索引。
最佳答案
通常(还有其他选项)数据库索引是 B 树,这意味着您可以对其进行范围扫描(包括相等扫描)。
条件 app_user_id % 10 = 0
无法通过单个范围扫描进行评估,这就是数据库可能不使用索引的原因。
它仍然可以决定以另一种方式使用索引,即进行完整扫描:读取整个表比读取整个索引花费更多时间。另一方面,读完索引后,您可能仍然会回到桌面,因此总体成本可能会更高。
这由数据库查询优化器决定。
一些例子:
select app_user_id from t where app_user_id % 10 = 0
在这里,您根本不需要表,所有必要的数据都在索引中。数据库很可能会进行完整索引扫描。
select count(*) from t where app_user_id % 10 = 0
同样。完整索引扫描。
select count(*) from t
只有当 app_user_id 不为 NULL 时,才可以通过索引来完成此操作(因为 NULL 数据不在索引中,至少在 Oracle 上,至少在单列索引上,您的数据库可能会以不同的方式处理此问题)。
某些数据库不需要为此访问表或索引,它们会在元数据中维护行计数。
select * from t where app_user_id = 5
这是索引的经典场景。数据库可以查看索引树的一小部分,检索一小部分(如果这是唯一索引或主索引,则只有一个)rowid,并有选择地从表中获取它们。
select * from t where app_user_id between 5 and 10
又一个经典索引案例。树中的范围扫描返回少量的 rowid 以从表中获取。
select * from t where app_user_id between 5 and 10 order by app_user_id
由于索引扫描返回有序数据,您甚至可以免费获得排序。
select * from t where app_user_id between 5 and 1000000000
也许在这里您不应该使用索引。似乎匹配了太多记录。在这种情况下,让绑定(bind)变量隐藏数据库中的范围实际上可能是有害的。
select * from t where app_user_id between 5 and 1000000000
order by app_user_id
但是在这里,由于排序会非常昂贵(甚至占用临时交换磁盘空间),也许按索引顺序迭代是好的。也许吧。
select * from t where app_user_id % 10 = 0
这很难决定。我们需要所有列,因此最终查询需要接触表。问题是是否要先遍历索引。该查询返回整个表的大约 10%。对于高效的索引访问路径来说,这可能太多了。如果优化器有理由相信查询返回的数据远少于表的 10%,那么在访问表之前进行索引扫描可能会比较好。如果表非常分散(大量删除的行占用空间),情况也是如此。
关于sql - SQL 中 WHERE 子句的索引性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3974653/