如何使用嵌套的 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?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JOINS 和嵌套 SELECT 优化此 SQL 查询?

如何优化这个嵌套的 SQL SELECT 查询

子查询(嵌套子查询)

sql子查询 嵌套SELECT语句

sql子查询 嵌套SELECT语句

sql子查询 嵌套SELECT实用语句