2016-06-09 70 views
2

這裏是我的查詢:如何用UNION運算符替換OR運算符?

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通常會阻止有效使用索引。所以當有大量數據時,我的查詢性能非常弱。

我該如何改進?其實我試圖用UNION替換OR,但正如你看到我的查詢太長了..那麼有什麼想法嗎?


編輯:這裏是EXPLAIN結果:(一個非常短的數據集 - 在整個20行)

enter image description here

回答

1

我想嘗試的第一件事是子查詢:

from ((select q.* from quanda q where q.id = :id1) union 
     (select q.* from quanda q where q.related = :id2) 
    ) left join 
    . . . 

注意:這真的希望inde xes在quanda(id)quanda(related)表現。

如果選擇幾行,那麼這可能會快得多。

+0

嗯,我知道了有些..給予好評 –

+0

但發生了什麼'WHERE'條款我的查詢?也是的,通常選擇幾行*(最多5行)* ..!可否請將您的想法應用到我的原始查詢中並將其添加到您的答案中? –

+0

@Stack。 。 。你會刪除'where'子句。這是多餘的,因爲它在子查詢中。 –

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 ALL更換UNION

+0

是的這項工作。我已經測試過了..只要你知道,那會讓我感到恐慌。我最初的查詢太長了,你的**真的太長了** :-)'..我寧願不要使用它。不管怎樣,謝謝你。 –

+0

如果你想採用'UNION'而不是忽略'OR'邏輯,查詢中的字符數實際上會增加一倍。 – 1000111

+0

真的..好的,謝謝你upvote –

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 
+0

請注意,這與Gordon Linoff的建議類似,除非我不推薦使用'select *' –

+0

我看到。謝謝upvote –

+0

順便說一句,我還建議你看看select子句中的那些相關的子查詢,他們可能是性能問題的原因。 –