在庞大的数据集上使用 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 事务)。有人可以支持EXISTSIN;据我记得mysql生成(或者至少5.1是这样的)......

a 上使用索引的效率取决于子查询结果的数量和顺序(假设优化器选择先从子查询中获取结果,然后将其与a 进行比较)。据我了解,最快的选择是执行合并连接,这需要两个结果集按相同的键排序;但是,由于排序顺序不同,这可能是不可能的。然后我猜这是优化器决定是排序还是使用循环连接。您可以依靠它的选择或尝试使用提示,看看它是否有所作为。

【讨论】:

1)我的查询只是一个SELECTserializable 事务与它有什么关系? 2)我在第二段中并没有真正关注你(也许我缺乏背景)。但我猜如果a=2 我们只有一次遍历。如果a IN (3,4,5,6,7,8....1000)我们有什么样的遍历呢? 1.当您只有一个查询时,可以保证您将获得一致的数据 - 假设在执行您的选择期间,有人可能从所涉及的任一表(table1table2)中插入/更新/删除记录。使用循环时会有所不同。假设您首先执行子查询,然后遍历结果集,将参数传递给循环中的另一个查询并执行它。每个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 是个好主意吗?的主要内容,如果未能解决你的问题,请参考以下文章

将验证/测试数据与训练数据混合是个好主意吗?

直接在数据库上处理大量数据是个好主意吗?

在mysql中索引日期时间字段是个好主意吗?

软删除是个好主意吗? [复制]

在 Web 服务 URL 中使用加密的数据库 ID 而不是 UUID 是个好主意吗?

通过 express req 参数传递解码的 jwt 数据是个好主意吗?