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

Posted

技术标签:

【中文标题】MySQL 优化查询,其中主键也在几乎相同的选择中【英文标题】:MySQL optimize query where primary key also in almost the same selection 【发布时间】:2017-01-12 11:04:49 【问题描述】:

我需要从具有“早上”事件的聊天(“普通”和“服务”类型)中选择 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'
)

架构和选择结果:

【问题讨论】:

您为什么不喜欢您的解决方案?运行需要多长时间?有多少记录?如果您希望我们优化您的查询,请分享您在受影响表上的所有索引以及查询解释的结果。 我们还没有太多字段;) 只有主键和外键字段被索引。我认为 where 子句中的 IF-ELSE 可以用来代替子查询,但很有趣 - 什么在数百万行上运行得更快? 【参考方案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 (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'
);

(您还应该明确指出您正在选择的属性)

【讨论】:

谢谢,你不会相信,但我忘记了“NOT”;)【参考方案2】:

我会在where 条件中使用union 而不是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  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&lt;&gt;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' 

【讨论】:

以上是关于MySQL 优化查询,其中主键也在几乎相同的选择中的主要内容,如果未能解决你的问题,请参考以下文章

mysql数据库中自动增长的主键也可以手动插入值吗?如何插入

如何为MySQL查询优化选择最佳索引

【Mysql】查询优化——减少回表操作

当外键也是主键时,在 MySQL 上出现外键错误? [复制]

MySQL 索引未按预期使用

优化 MySQL 查询