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<>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数据库中自动增长的主键也可以手动插入值吗?如何插入