这是我的问题:
SELECT
h.id,
h.subject,
h.body matnF,
h.amount,
h.keywords tags,
h.closed,
h.author_id author,
h.AcceptedAnswer,
h.type,
h.visibility,
h.date_time,
v.value AS vote_value,
u.reputation,
u.user_fname,
u.user_lname,
u.avatar,
(h.author_id = user_id1) as hasUserId,
(select COALESCE(sum(vv.value), 0)
from votes vv
where h.id = vv.post_id and vv.table_code = '$this->table_code'
) as total_votes,
(select count(1)
from favorites ff
where h.type = 0 and h.id = ff.post_id and ff.table_code = '$this- >table_code'
) as total_favorites,
CASE WHEN h.type = 1 THEN '0'
WHEN h.amount IS NULL OR h.author_id = :user_id2 THEN '1'
ELSE EXISTS( select 1
from money m
where m.user_id = :user_id3 and m.post_id = h.id
)END paid,
CASE WHEN h.type = 0 AND f.id IS NOT NULL THEN '2'
ELSE '3'
END AS favorite
FROM qanda h
LEFT JOIN votes v ON h.id = v.post_id AND v.user_id = :user_id4 AND v.table_code = '$this->table_code'
LEFT JOIN favorites f ON h.type = 0 AND h.id = f.post_id AND f.user_id = :user_id5 AND f.table_code = '$this->table_code'
LEFT JOIN users u ON h.author_id = u.id and h.visibility = 1
WHERE h.id = :id1 OR h.related = :id2
ORDER BY h.type , h.AcceptedAnswer DESC , h.date_time
LIMIT 20;
请关注上面的这一行查询:
WHERE h.id = :id1 OR h.related = :id2
如您所见,这两个条件之间存在OR
逻辑运算符。如您所知,OR
通常会阻止索引的有效使用。因此,当存在大量数据时,我的查询的性能确实很弱。
我该如何改进?实际上我正试图用OR
替换UNION
,但是当你看到我的查询太长时......那么有什么想法吗?
编辑:以下是EXPLAIN
:的结果(对于非常短的数据集 - 总共20行)
答案 0 :(得分:1)
我要尝试的第一件事是子查询:
from ((select q.* from quanda q where q.id = :id1) union
(select q.* from quanda q where q.related = :id2)
) left join
. . .
注意:这确实需要quanda(id)
和quanda(related)
上的索引来提高性能。
如果选择了很少的行,那么这可能会快得多。
答案 1 :(得分:1)
你可以尝试一下:
SELECT h.id, h.subject, h.body matnF, h.amount, h.keywords tags, h.closed, h.author_id author, h.AcceptedAnswer, h.type, h.visibility, h.date_time, v.value AS vote_value, u.reputation, u.user_fname, u.user_lname, u.avatar, (h.author_id = :user_id1) as hasUserId,
(select COALESCE(sum(vv.value),0) from votes vv where h.id = vv.post_id and vv.table_code = '$this->table_code') as total_votes,
(select count(1) from favorites ff where h.type = 0 and h.id = ff.post_id and ff.table_code = '$this->table_code') as total_favorites,
CASE WHEN h.type = 1 THEN '0'
WHEN h.amount IS NULL OR h.author_id = :user_id2 THEN '1'
ELSE EXISTS (select 1 from money m where m.user_id = :user_id3 and m.post_id = h.id) END paid,
CASE WHEN h.type = 0 AND f.id IS NOT NULL THEN '2' ELSE '3' END AS favorite
FROM qanda h
LEFT JOIN votes v ON h.id = v.post_id AND v.user_id = :user_id4 AND v.table_code = '$this->table_code'
LEFT JOIN favorites f ON h.type = 0 AND h.id = f.post_id AND f.user_id = :user_id5 AND f.table_code = '$this->table_code'
LEFT JOIN users u ON h.author_id = u.id and h.visibility = 1
WHERE h.id = :id1
ORDER BY h.type, /*(tans.id IS NOT NULL) DESC,*/ h.AcceptedAnswer DESC, h.date_time
LIMIT 20
UNION
SELECT
*
FROM
(SELECT h.id, h.subject, h.body matnF, h.amount, h.keywords tags, h.closed, h.author_id author, h.AcceptedAnswer, h.type, h.visibility, h.date_time, v.value AS vote_value, u.reputation, u.user_fname, u.user_lname, u.avatar, (h.author_id = :user_id1) as hasUserId,
(select COALESCE(sum(vv.value),0) from votes vv where h.id = vv.post_id and vv.table_code = '$this->table_code') as total_votes,
(select count(1) from favorites ff where h.type = 0 and h.id = ff.post_id and ff.table_code = '$this->table_code') as total_favorites,
CASE WHEN h.type = 1 THEN '0'
WHEN h.amount IS NULL OR h.author_id = :user_id2 THEN '1'
ELSE EXISTS (select 1 from money m where m.user_id = :user_id3 and m.post_id = h.id) END paid,
CASE WHEN h.type = 0 AND f.id IS NOT NULL THEN '2' ELSE '3' END AS favorite
FROM qanda h
LEFT JOIN votes v ON h.id = v.post_id AND v.user_id = :user_id4 AND v.table_code = '$this->table_code'
LEFT JOIN favorites f ON h.type = 0 AND h.id = f.post_id AND f.user_id = :user_id5 AND f.table_code = '$this->table_code'
LEFT JOIN users u ON h.author_id = u.id and h.visibility = 1
WHERE h.id = :id2
ORDER BY h.type, /*(tans.id IS NOT NULL) DESC,*/ h.AcceptedAnswer DESC, h.date_time
LIMIT 20) t;
注意:如果您希望在最终结果集中允许重复,请将UNION
替换为UNION ALL
。
答案 2 :(得分:1)
如果要从使用UNION而不是OR获得任何优势,那么使查询的那部分尽可能小,特别是将列数保持最小。在任何其他联接之前做联盟。我建议这个:
SELECT
h.id
, h.subject
, h.body AS matnF
, h.amount
, h.keywords AS tags
, h.closed
, h.author_id AS author
, h.AcceptedAnswer
, h.type
, h.visibility
, h.date_time
, v.value AS vote_value
, u.reputation
, u.user_fname
, u.user_lname
, u.avatar
, h.author_id AS hasUserId
, ( SELECT COALESCE(SUM(vv.value), 0)
FROM votes vv
WHERE h.id = vv.post_id
AND vv.table_code = '$this->table_code'
) AS total_votes
, ( SELECT COUNT(1)
FROM favorites ff
WHERE h.type = 0 AND h.id = ff.post_id
AND ff.table_code = '$this- >table_code'
) AS total_favorites
, CASE
WHEN h.type = 0 AND f.id IS NOT NULL THEN '2'
ELSE '3'
END AS favorite
, CASE
WHEN h.type = 1 THEN '0'
WHEN h.amount IS NULL OR h.author_id = :user_id2 THEN '1'
ELSE EXISTS( select 1
from money m
where m.user_id = :user_id3 and m.post_id = h.id
)
END paid
FROM (
SELECT q.id , q.author_id , q.visibility , q.type FROM quanda q WHERE q.id = :id1
UNION
SELECT q.id , q.author_id , q.visibility , q.type FROM quanda q WHERE q.related = :id2
) h
LEFT JOIN votes v ON h.id = v.post_id
AND v.user_id = :user_id4
AND v.table_code = '$this->table_code'
LEFT JOIN favorites f ON h.type = 0
AND h.id = f.post_id
AND f.user_id = :user_id5
AND f.table_code = '$this->table_code'
LEFT JOIN users u ON h.author_id = u.id
AND h.visibility = 1