2016-06-09 5 views
2

Вот мой запрос:Как заменить оператор OR с оператором UNION?

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, но, как вы видите, мой запрос слишком длинный. Так есть идея?


EDIT: Вот результат 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

Ну, я получил его несколько .. upvote –

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

Примечание: Replace UNION по UNION ALL, если вы хотите, чтобы дубликаты в окончательном наборе результатов.

+0

Да, эта работа. Я уже тестировал это. Просто вы знаете, это пугает меня. Мой первоначальный запрос слишком длинный, а ваш ** действительно очень длинный. :-). .. Я предпочитаю не использовать его. в любом случае, спасибо. –

+0

количество символов в запросе фактически удваивается, если вы хотите принять 'UNION' вместо того, чтобы исключить логику' OR'. – 1000111

+0

True .. Ok спасибо 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

Обратите внимание, что это похоже на рекомендацию Гордона Линоффа, за исключением того, что я не рекомендую использовать 'select *' –

+0

. Спасибо upvote –

+0

Кстати, я также предлагаю вам посмотреть на эти коррелированные подзапросы в предложении select, что может стать причиной проблем с производительностью. –