在庞大的数据集上使用 IN 是个好主意吗?
Posted
技术标签:
【中文标题】在庞大的数据集上使用 IN 是个好主意吗?【英文标题】:Is using an IN over a huge data set a good idea? 【发布时间】:2013-08-10 20:55:24 【问题描述】:假设我有一个表单查询:
SELECT a, b, c, d
FROM table1
WHERE a IN (
SELECT x
FROM table2
WHERE some_condition);
现在IN
的查询可以返回大量记录。
假设a
是主键,那么使用索引是编写此类查询的最佳方式吗?
或者循环遍历子查询返回的每条记录会更优化?
对我来说,很明显,当我执行where a = X
时,我只是进行索引(树)遍历。
但我不确定IN
(尤其是在庞大的数据集上)将如何遍历/利用索引。
【问题讨论】:
这是与数据库无关的问题,还是您有特定的 RDBMS? @OzrenTkalcecKrznaric:我已将其标记为mysql
,因为这是我使用的。所以这不是一个笼统的问题。
【参考方案1】:
MySQL 优化器还没有真正准备好(jet)来正确处理这个问题,你应该将这种查询重写为 iNNER JOIN 并正确索引这将是假设 t1.a 和 t2.x 是唯一的禁食方法
类似的东西。
SELECT
a
, b
, c
, d
FROM
table1 as t1
INNER JOIN
table2 as t2
ON t1.a = t2.x
WHERE
t1.some_condition ....
并确保 t1.a 和 t2.x 具有 PRIMARY 或 UNIQUE 索引
【讨论】:
您的查询不会给出与 OP 相同的结果(仅当table2.x
唯一时才会返回相同的结果;否则您会得到重复...)。【参考方案2】:
使用 1 个查询而不是循环肯定会更有效(并且本质上是一致的,要获得与循环一致的结果,通常您必须使用 serializable
事务)。有人可以支持EXISTS
与IN
;据我记得mysql生成(或者至少5.1是这样的)......
在a
上使用索引的效率取决于子查询结果的数量和顺序(假设优化器选择先从子查询中获取结果,然后将其与a
进行比较)。据我了解,最快的选择是执行合并连接,这需要两个结果集按相同的键排序;但是,由于排序顺序不同,这可能是不可能的。然后我猜这是优化器决定是排序还是使用循环连接。您可以依靠它的选择或尝试使用提示,看看它是否有所作为。
【讨论】:
1)我的查询只是一个SELECT
。serializable
事务与它有什么关系? 2)我在第二段中并没有真正关注你(也许我缺乏背景)。但我猜如果a=2
我们只有一次遍历。如果a IN (3,4,5,6,7,8....1000)
我们有什么样的遍历呢?
1.当您只有一个查询时,可以保证您将获得一致的数据 - 假设在执行您的选择期间,有人可能从所涉及的任一表(table1
或 table2
)中插入/更新/删除记录。使用循环时会有所不同。假设您首先执行子查询,然后遍历结果集,将参数传递给循环中的另一个查询并执行它。每个table1
/table2
中的数据可能与您在执行开始时的数据不同。这可能是也可能不是问题(例如,您不知道任何更改,或者禁止更新)...
2. a=2
应该进行索引搜索(或在 INNODb 表的 PK 情况下进行聚集索引搜索)。 a IN (3,4,....1000)
应该是 [clustered]index range scan
(或者如果你的表非常小,则进行全表扫描)。但它与你输入 WHERE a IN (SELECT ....)
时得到的不同。查询不是按照我们在 SQL 中编写的顺序执行的。我们可以通过提示在一定程度上控制物理执行(至少顺序),但这取决于优化器首先访问哪个表。
Queries are not executed in the order we write them in SQL
,是的,但在这种情况下,必须执行内部选择在执行外部选择之前。
在您的第二条评论中,我的印象是您暗示由于在IN()
中使用了查询,因此在这种情况下不能直接使用索引以上是关于在庞大的数据集上使用 IN 是个好主意吗?的主要内容,如果未能解决你的问题,请参考以下文章