2016-06-09 40 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子句中的那些相关的子查询,他们可能是性能问题的原因。 –