如何使用嵌套的 SELECT 子查询优化 UPDATE?
Posted
技术标签:
【中文标题】如何使用嵌套的 SELECT 子查询优化 UPDATE?【英文标题】:How to optimize UPDATE with a nested SELECT subquery? 【发布时间】:2019-06-10 01:24:29 【问题描述】:我编写了一个复杂的 UPDATE 查询,它可以工作,但看起来很危险。这是我正在尝试做的事情:
在每个主题中,用户“Bob123”匿名发布。当您在某个主题中匿名发帖时,您会获得该主题的唯一匿名索引。
假设我想将两个主题合并在一起。 Bob123 在两个主题中都有不同的匿名索引,因此他的唯一匿名索引不会是唯一的。我只有两条数据要处理:$topic_id,您要合并到的主题 ID,和 $post_id_list,所有合并的帖子 ID。
我想更新该主题中每个不同的poster_id 帖子的所有anonymous_index 条目。这个anonymous_index 需要是他们在其他主题被合并之前在主题中的原始索引。
第一个 SELECT 查询首先选择已移动帖子的匿名索引。
外部 SELECT 查询获取主题中这些合并海报的第一个非合并帖子的匿名索引(如果是 > 0
),并从第一个查询中选择一个合并的匿名索引。
然后,我更新它。只要该主题中这些海报的匿名索引不等于旧索引,我就会对其进行更新。
我在这里缺少什么简单的东西吗?我不喜欢子查询中有子查询这一事实。
起初我使用HAVING MIN(anonymous_index) <> MAX(anonymous_index)
和AND post_id NOT IN ($merged_post_list)
来选择需要更新的海报ID 列表和未合并的匿名索引,但它返回了0 行。如果合并后的帖子在所有原始帖子之前(并且具有更大的匿名索引),则最小匿名索引将匹配该海报的最大索引。所以制作另一个子查询修复了这个...
$merged_post_list = implode(',', $post_id_list);
...
UPDATE " . POSTS_TABLE . " AS p
INNER JOIN ( SELECT p.post_id, p.anonymous_index AS old_index,
merged.poster_id, merged.anonymous_index AS new_index
FROM " . POSTS_TABLE . " AS p,
( SELECT poster_id, anonymous_index
FROM " . POSTS_TABLE . "
WHERE post_id IN ($merged_post_list)
AND topic_id = $topic_id
AND anonymous_index > 0
) AS merged
WHERE p.post_id NOT IN ($merged_post_list)
AND p.topic_id = $topic_id
AND p.anonymous_index > 0
AND p.poster_id = merged.poster_id
GROUP BY merged.poster_id
) AS postdata
SET p.anonymous_index = postdata.old_index
WHERE p.topic_id = $topic_id
AND anonymous_index > 0
AND anonymous_index <> postdata.old_index
AND p.poster_id = postdata.poster_id
post_id 是主索引,poster_id 和 topic_id 也是索引。
以下是一些示例行为:
合并前:
|post_id_____poster_id_____anonymous_index|
| 11 | 3 | 2 |
| 12 | 22 | 1 |
| 14 | 22 | 1 |
| 15 | 3 | 2 |
合并后:
|post_id_____poster_id_____anonymous_index|
| 10 | 22 | 4 |
| 11 | 3 | 2 |
| 12 | 22 | 1 |
| 13 | 3 | 4 |
| 14 | 22 | 1 |
| 15 | 3 | 2 |
| 16 | 22 | 4 |
更新后(上述查询):
|post_id_____poster_id_____anonymous_index|
| 10 | 22 | 1 |
| 11 | 3 | 2 |
| 12 | 22 | 1 |
| 13 | 3 | 2 |
| 14 | 22 | 1 |
| 15 | 3 | 2 |
| 16 | 22 | 1 |
编辑:我创建了以下索引和一个替代 SELECT 查询以避免有两个子查询,这些会怎么样?:
(topic_id, poster_id, anonymous_index, post_id)
SELECT p.post_id, p.anonymous_index AS old_index,
merged.poster_id, merged.anonymous_index AS new_index
FROM " . POSTS_TABLE . " AS p,
" . POSTS_TABLE . " AS merged
WHERE p.topic_id = $topic_id
AND p.anonymous_index > 0
AND p.post_id NOT IN ($post_list)
AND p.poster_id = merged.poster_id
AND merged.topic_id = $topic_id
AND merged.anonymous_index > 0
AND merged.post_id IN ($post_list)
GROUP BY merged.poster_id
ORDER BY NULL
【问题讨论】:
我认为你试图让事情复杂化...... 发布您的确切问题,我们可以为此编写适当的查询,而无需这些冗余子查询... 我添加了一个新的 SELECT 查询来替换内部连接中的旧查询,并创建了一个索引来配合它。另外,我认为我确实很清楚地解释了这个问题。我什至包括了 3 个表来查看合并前的数据、合并损坏的数据后和修复后的结果。 【参考方案1】:考虑跨三个自联接的内部联接更新:
UPDATE " . POSTS_TABLE . " AS final
INNER JOIN " . POSTS_TABLE . " AS p
ON p.poster_id = final.poster_id
AND p.topic_id = final.topic_id
AND p.topic_id = $topic_id
AND p.post_id NOT IN ($merged_post_list)
AND p.anonymous_index > 0
INNER JOIN " . POSTS_TABLE . " AS merged
ON merged.poster_id = p.poster_id
AND merged.topic_id = p.topic_id
AND merged.topic_id = $topic_id
AND merged.post_id IN ($merged_post_list)
AND merged.anonymous_index > 0
SET final.anonymous_index = p.anonymous_index
WHERE final.anonymous_index > 0
AND final.anonymous_index <> p.anonymous_index
【讨论】:
我编辑了 OP 并在其底部添加了一个新的 SELECT 查询。如果我创建了索引:(topic_id, poster_id, anonymous_index, post_id)
并用我添加到 OP 底部的内容替换了内部 sql 查询呢?以上是关于如何使用嵌套的 SELECT 子查询优化 UPDATE?的主要内容,如果未能解决你的问题,请参考以下文章