通过 LEFT JOIN 优化 SQL 子查询
Posted
技术标签:
【中文标题】通过 LEFT JOIN 优化 SQL 子查询【英文标题】:Optimizing SQL subquery through a LEFT JOIN 【发布时间】:2015-09-16 18:32:17 【问题描述】:我想根据 uniqueEntries 中不存在的实际条目 User_ID 将实际条目表中的所有记录插入到唯一条目表中。
我从一个包含NOT IN
子查询的sql子句开始,它非常慢(在操作400K记录时),然后将其变成了LEFT JOIN
子句,但速度并没有提高。
以下是我原来的包含NOT IN
子查询的sql子句:
INSERT INTO uniqueEntries
SELECT *
FROM actualEntries
WHERE actualEntries.User_ID NOT IN (
SELECT uniqueEntries.User_ID
FROM uniqueEntries
)
GROUP BY User_ID"
以下是转换成LEFT JOIN
后的sql子句:
INSERT INTO uniqueEntries
SELECT actualEntries.*
FROM actualEntries
LEFT JOIN uniqueEntries
ON uniqueEntries.User_ID = actualEntries.User_ID
WHERE uniqueEntries.User_ID IS NULL
GROUP BY User_ID
当我对 50 条记录运行这两个查询时,它们会立即完成,但是当我对 400K 记录运行它们时,它们不会完成。
完成此操作的最快方法是什么?
更新/解决方案: 根据@Rahul、@Steve E 和@fhthiella,我将 LEFT JOIN 更新如下,并将 470K 记录的处理时间减少到 2 分钟。
INSERT INTO uniqueEntries
SELECT actualEntries.*
FROM actualEntries
LEFT JOIN uniqueEntries
ON uniqueEntries.id = actualEntries.id
WHERE uniqueEntries.User_ID IS NULL GROUP BY User_ID
【问题讨论】:
【参考方案1】:在 uniqueEntries.User_ID 上放置唯一键或主键。那么
INSERT IGNORE INTO uniqueEntries
SELECT actualEntries.*
FROM actualEntries
IGNORE 子句将使 mysql 在插入过程中跳过错误。 这就是the manual 所说的:
如果您使用 IGNORE 关键字,则会出现错误 执行 INSERT 语句时发生的事件将被忽略。为了 例如,没有 IGNORE,复制现有 UNIQUE 的行 表中的索引或 PRIMARY KEY 值导致重复键错误 并且该语句被中止。使用 IGNORE,该行被丢弃并且没有 发生错误。忽略的错误可能会生成警告,尽管 重复键错误不会。
【讨论】:
Insert Ignore 部分帮助了我的请求。【参考方案2】:首先删除GROUP BY
子句GROUP BY User_ID
,因为它根本不需要。此外,您应该在 User_ID
列上为表 uniqueEntries
和 actualEntries
建立索引,因为您将其用作连接列。这样,您的查询应该看起来像
INSERT INTO uniqueEntries
SELECT actualEntries.*
FROM actualEntries
LEFT JOIN uniqueEntries
ON uniqueEntries.User_ID = actualEntries.User_ID
WHERE uniqueEntries.User_ID IS NULL
【讨论】:
我将 uniqueEntries 和 actualEntries 上的 .User_ID 更改为 .id(有效)。我确实需要 GROUP BY,因为有些条目是重复的,我只想要唯一的条目 @xited,我仍然相信你不需要GROUP BY
因为 where 语句 WHERE uniqueEntries.User_ID IS NULL
会按照你说的做。
@Rahul 不完全是 sqlfiddle.com/#!9/096ed8/1 在这种情况下仍然需要 group by(或者最好使用主键,然后使用 INSERT IGNORE sqlfiddle.com/#!9/e3131/1
@Rahul:uniqueEntries 不包含具有相同 User_ID 的多条记录,但 actualEntries 包含。 GROUP BY 应该确保我只在从输出中删除 uniqueEntries 后才从 actualEntries 中提取唯一记录,对吧?
@xited,那么是的,您可能需要一个分组;但我会首先使用 group by 获得分组结果,然后使用 unique_entries 执行连接。否则,使用insert ignore ...
构造。【参考方案3】:
您应该在 uniqueEntries.User_ID 和 actualEntries.User_ID 字段上添加索引:
ALTER TABLE uniqueEntries ADD INDEX idx_ue_id (User_ID);
ALTER TABLE actualEntries ADD INDEX idx_ae_id (User_ID);
这应该使连接更快。我还看到您正在选择所有表格字段:
SELECT actualEntries.*
但是你是按 User_id 分组的
GROUP BY User_ID
我认为您这样做是因为每个 User_ID 可能有多行。 MySQL 允许您这样做,但请注意,如果有多行,您的查询将只保留一个,但未分组的值将是不确定的(它们可以属于任何分组的行)。
【讨论】:
@fhthiella 是多行。没错,我不在乎选择哪一个,只要我检索到唯一的行即可。 @xited 好的,所以您可以使用这样的 GROUP BY,只要您知道自己在做什么;)但是如果您希望 uniqueEntries 的 User_ID 是唯一的,我认为您应该接受 steve e 回答,因为它更优雅!以上是关于通过 LEFT JOIN 优化 SQL 子查询的主要内容,如果未能解决你的问题,请参考以下文章
HiveSql&SparkSql —— 使用left semi join做inexists类型子查询优化
mysql 优化慢复杂sql (多个left join 数量过大 order by 巨慢)