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

РЕДАКТИРОВАТЬ: Вот результатEXPLAIN: (для действительно короткого набора данных - всего 20 строк)

Ответы на вопрос(3)

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
 Used_By_Already10 июн. 2016 г., 00:16
да. извини не должно быть, уберу
 Martin AJ09 июн. 2016 г., 13:24
Понимаю. Спасибо upvote
 Martin AJ09 июн. 2016 г., 13:26
Вы имеете в виду эти скобки?
 Used_By_Already09 июн. 2016 г., 13:24
Обратите внимание, что это похоже на рекомендацию Гордона Линоффа, за исключением того, что я бы не рекомендовал использоватьselect *
 Used_By_Already09 июн. 2016 г., 13:25
Кстати, я также предлагаю вам взглянуть на эти взаимосвязанные подзапросы в предложении select, они могут быть причиной проблем с производительностью.
 Used_By_Already09 июн. 2016 г., 13:27
например это один из взаимосвязанных подзапросовSELECT COALESCE(SUM(vv.value), 0) FROM votes vv WHERE h.id = vv.post_id AND vv.table_code = '$this->table_cod
 Martin AJ09 июн. 2016 г., 13:28
Хорошо, что с этим не так?
 Used_By_Already09 июн. 2016 г., 13:32
Я просто сказал «посмотрите» или «подумайте» о каждом из них, потому что они МОГУТ БЫТЬ причиной медлительности. Примечание. Я не говорю, что мой запрос идеален, просто если вы хотите получить преимущество от объединения, сделайте список столбцов как можно короче, я думаю, что пропустил какой-то столбец, который может вам понадобиться во внешнем запросе.
 Martin AJ09 июн. 2016 г., 13:35
Понял .. спасибо снова .. И да, вам не хватает некоторых столбцов для выбора во внутреннем запросе, а также чтоLIMIT а такжеORDER BY .. Однако это не имеет значения. Я понял это ..
 Martin AJ09 июн. 2016 г., 14:46
Кстати почемуWHERE пункт еще там? Кажется бесполезным для меня. Я прав?

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 если вы хотите разрешить дубликаты в вашем конечном наборе результатов.

 100011109 июн. 2016 г., 13:04
Как насчет производительности с помощьюUNION ?
 100011109 июн. 2016 г., 12:55
количество символов в запросе фактически удваивается, если вы хотите принятьUNION вместо того, чтобы опуститьOR логика.
 Martin AJ09 июн. 2016 г., 12:57
Правда .. хорошо спасибо upvote
 Martin AJ09 июн. 2016 г., 12:54
Да это работа. Я уже проверил это .. Только вы знаете, это пугает меня. Мой оригинальный запрос слишком длинный, а вашдействительно очень слишком долго:-) .. Я предпочитаю не использовать это. в любом случае, спасибо.
 100011109 июн. 2016 г., 13:02
Вы можете попробовать, используяIN лайкWHERE h.id IN (:id1,:id2)...., И проверьтеEXPLAIN результат.
 Martin AJ09 июн. 2016 г., 13:07
UNION делает намного быстрее ..! На самом деле я проверил это, прежде чем спрашивать. Но, как вы видите, если я хочу использовать UNION, запрос будет очень большим. Подход Гордона тоже кажется хорошим, я еще не решил, какой из них выбрать.
 Martin AJ09 июн. 2016 г., 13:04
Я сделал .. это становится лучше, но не намного ..! Тем не менее это медленно для огромных данных.
Решение Вопроса

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) для исполнения.

Если выбрано несколько строк, это может быть намного быстрее.

 Martin AJ09 июн. 2016 г., 12:55
Ну, я получил это несколько .. upvote
 Gordon Linoff09 июн. 2016 г., 17:48
@ Стеки. , , Вы бы удалилиwhere пункт. Это избыточно, потому что это в подзапросе.
 Martin AJ09 июн. 2016 г., 14:33
Но что происходит сWHERE пункт в моем запросе? И также да, обычно выбирается несколько строк(Максимум 5 рядов) ..! Можете ли вы применить свою идею к моему первоначальному запросу и добавить ее в свой ответ?
 Martin AJ09 июн. 2016 г., 17:50
Да ты прав ..! Но я не думаю, что с помощьюUNION сделать что-нибудь лучше.stackoverflow.com/questions/37729731/...

Ваш ответ на вопрос