MySQL优化查询,其中主键也在几乎相同的选择中

时间:2017-01-12 11:04:50

标签: mysql optimization query-optimization

我需要从具有“早晨”事件的聊天(“普通”和“服务”类型)中选择user.id = 1的所有消息(传入和传出)

显示它们(附加图像上的选择1)

SELECT * 
FROM `message`
JOIN chat
ON message.chat_id = chat.id
JOIN event
ON chat.event_id = event.id
JOIN chat_type
ON chat.chat_type_id = chat_type.id
WHERE (sender = 1 or recipient = 1)
AND event.name = 'morning'

但我必须排除“服务”消息,我是发件人。 这很好,但我不喜欢它(在图像上选择2)

SELECT * 
FROM `message`
JOIN chat
ON message.chat_id = chat.id
JOIN event
ON chat.event_id = event.id
JOIN chat_type
ON chat.chat_type_id = chat_type.id
WHERE (sender = 1 or recipient = 1)
AND event.name = 'morning'
AND message.id NOT IN (
    SELECT message.id 
    FROM `message`
    JOIN chat
    ON message.chat_id = chat.id
    JOIN event
    ON chat.event_id = event.id
    JOIN chat_type
    ON chat.chat_type_id = chat_type.id
    WHERE (sender = 1 or recipient = 1)
    AND sender = 1 
AND chat_type.name = 'service'
)

架构并选择结果:

enter image description here

2 个答案:

答案 0 :(得分:0)

我会在union条件中使用or而不是where条件,然后您可以在没有子查询的情况下进行过滤:

    (
        SELECT  *
            FROM  `message`
            JOIN  chat  ON message.chat_id = chat.id
            JOIN  event  ON chat.event_id = event.id
            JOIN  chat_type  ON chat.chat_type_id = chat_type.id
            WHERE  sender = 1
              AND  event.name = 'morning'
              AND  chat_type.name <> 'service'
    )
    UNION  
    (
        SELECT  *
            FROM  `message`
            JOIN  chat  ON message.chat_id = chat.id
            JOIN  event  ON chat.event_id = event.id
            JOIN  chat_type  ON chat.chat_type_id = chat_type.id
            WHERE  recipient = 1
              AND  event.name = 'morning'
    ) 

MySQL目前无法使用索引来满足or操作,因此使用union是一种常见的优化技术。我还会考虑从查询中排除chat_type表,并在chat_type_id<>2上过滤。沿着同样的路线,您也可以摆脱event表。即使您决定保留此表,也应该对数字ID字段进行过滤,而不是对文本值进行过滤。

或者,您可以调整or条件以在没有子查询的情况下进行过滤:

SELECT  *
    FROM  `message`
    JOIN  chat  ON message.chat_id = chat.id
    JOIN  event  ON chat.event_id = event.id
    JOIN  chat_type  ON chat.chat_type_id = chat_type.id
    WHERE  ((sender = 1
                      AND  chat_type.name <> 'service')
              or  recipient = 1
           )
      AND  event.name = 'morning' 

答案 1 :(得分:0)

你应该解释为什么你不喜欢你的解决方案。

根据您的描述和示例,子选择是多余的且无效的:

SELECT * 
FROM `message`
JOIN chat
  ON message.chat_id = chat.id
JOIN event
  ON chat.event_id = event.id
JOIN chat_type
  ON chat.chat_type_id = chat_type.id
WHERE (message.sender = 1 or message.recipient = 1)
AND event.name = 'morning'
AND NOT (
   message.sender = 1  /* or whatever your id is */ 
   AND chat_type.name = 'service'
);

(你还应该明确地调出你选择的属性)