在大多数情况下,当我尝试优化OR条件时,我试图了解UNION替代方案将如何执行。在许多情况下,UNION替代方案表现更好,因为它可以正确索引,而OR条件不能。
让我们假设以下示例:
SELECT
*
FROM
posts
WHERE
posts.id = (SELECT
postid
FROM
comments
WHERE
comments.postid = posts.id
AND (comments.userid = 5 OR comments.score = 8)
ORDER BY postid
LIMIT 1);
在这种情况下,将子查询转换为UNION子句并不是一件容易的事,因为这样做会使我们用另一个子查询包装整个UNION子句,以确保结果的顺序不会成为影响。 但是,当应用包装时,我们现在有一个3级嵌套查询,而不是2级查询,因此表帖子不再可用于UNION的内部SELECT查询,这使得语法无效。
这是我正在寻找的转变,希望能够发挥作用:
SELECT
*
FROM
posts
WHERE
posts.id = (
SELECT
*
FROM
((SELECT
comments.postid,
comments.postid
FROM
comments
WHERE
comments.postid = posts.id
AND (
comments.score = 8
)
ORDER BY
comments.postid LIMIT 1)
UNION
DISTINCT (SELECT
comments.postid,
comments.postid
FROM
comments
WHERE
comments.postid = posts.id
AND (comments.userid = 5)
ORDER BY
comments.postid LIMIT 1)
) AS union1
ORDER BY
union1.postid LIMIT 1)
因此,在这种情况下,MySQL只会返回此错误:Error Code: 1054. Unknown column 'posts.id' in 'where clause'
。
是否有创造性的方法将此OR条件转换为此类相关子查询中的UNION?
重要提示:我完全清楚这个查询可以以不同方式重写,而不需要原始子查询,这可能会使整个问题无关紧要。但是,为了这个讨论,我试图看看在我之后进行转换的最佳方式是什么,只需要进行最小的更改。
答案 0 :(得分:1)
与大多数相关的子查询一样(根据我的经验),这似乎更好地写成不相关的,如下所示:
SELECT *
FROM posts
WHERE posts.id IN (
SELECT postid
FROM comments
WHERE comments.userid = 5 OR comments.score = 8
)
;
或至少,作为存在
SELECT *
FROM posts
WHERE EXISTS (
SELECT *
FROM comments AS c
WHERE c.postid = posts.id
AND (c.userid = 5 OR c.score = 8)
)
;
虽然这并没有直接解决OR到UNION的转换,但它们的转换要简单得多。
转换:
SELECT *
FROM posts
WHERE posts.id IN (
SELECT postid
FROM comments
WHERE comments.userid = 5
UNION
SELECT postid
FROM comments
WHERE comments.score = 8
)
;
从技术上讲,第二个转换不需要/使用UNION,但它会使相关子查询加倍。
SELECT *
FROM posts
WHERE EXISTS (
SELECT *
FROM comments AS c
WHERE c.postid = posts.id
AND c.userid = 5
)
OR EXISTS (
SELECT *
FROM comments AS c
WHERE c.postid = posts.id
AND c.score = 8
)
;
答案 1 :(得分:0)
为什么不只是join
?
从这里开始:
SELECT p.*
FROM posts p
INNER JOIN comments c ON c.postid = posts.id
WHERE c.userid = 5 or c.score = 8
ORDER p.id
LIMIT 1;
我敢于你找到一种产生不同结果的情况,而且它已经可能比发布的结果快得多。
一旦你有了,你可以正常联合:
SELECT *
FROM posts
WHERE id IN (
SELECT p.id
FROM posts p
INNER JOIN comments c ON c.postid = posts.id
WHERE c.userid = 5
UNION
SELECT p.id
FROM posts p
INNER JOIN comments c ON c.postid = posts.id
WHERE c.score = 8
)
ORDER BY id
LIMIT 1;
OR
SELECT *
FROM (
SELECT p.*
FROM posts p
INNER JOIN comments c ON c.postid = posts.id
WHERE c.userid = 5
UNION
SELECT p.*
FROM posts p
INNER JOIN comments c ON c.postid = posts.id
WHERE c.score = 8
) t
ORDER BY id
LIMIT 1;
第一个选项可以更简单,甚至不需要连接:
SELECT *
FROM posts
WHERE id IN (
SELECT c.postid
FROM comments c
WHERE c.userid = 5
UNION
SELECT c.postid
FROM comments c
WHERE c.score = 8
)
ORDER BY id
LIMIT 1;