【问题标题】:MySQL Slow SubqueryMySQL慢子查询
【发布时间】:2012-09-23 00:43:43
【问题描述】:

我有一个支持票务系统。我正在添加一个部门部分,用户可以是多个部门的成员。

问题是我正在使用子查询来获取具有部门 ID 的工单,该部门 ID 位于该用户的部门访问表中。

这是子查询:

t.department_id IN (SELECT utd.department_id FROM users_to_departments utd WHERE utd.user_id = :department_or_assigned_or_user_id AND utd.site_id = :site_id)

这会减慢查询速度。一张包含 110,000 张票的表格大约需要 2 秒。子查询是原因。

我尝试将其转换为 LEFT JOIN,然后使用 HAVING utd.id IS NOT NULL,但速度更差。

我想知道是否可以将其转换为内部联接。

问题是我总是希望获得由该用户创建的工单,即使工单现在位于不同的部门。

目前在子查询之后使用以下内容:

OR (t.assigned_user_id = :department_or_assigned_or_user_id OR t.user_id = :department_or_assigned_or_user_id)

所有正确的列都被索引,所以 MySQL 不做任何文件排序等。

users_to_departments 表只是 user_id 和 department_id。

任何帮助都会很棒。

这是我的完整查询。

SELECT 
t.* , 
u.pushover_key AS `owner_pushover_key`, 
u.name AS `owner_name`, 
u.id AS `owner_id`, 
u.email AS `owner_email`, 
u.phone_number AS `owner_phone`, 
u.email_notifications AS `owner_email_notifications`, 
u2.pushover_key AS `assigned_pushover_key`, 
u2.name AS `assigned_name`, 
u2.id AS `assigned_id`, 
u2.email AS `assigned_email`, 
u2.email_notifications AS `assigned_email_notifications`, 
u3.name AS `submitted_name`, 
u3.id AS `submitted_id`, 
u3.email AS `submitted_email`, 
u3.email_notifications AS `submitted_email_notifications`, 
tp.name AS `priority_name`, 
td.name AS `department_name`, 
ts.name AS `status_name`, 
ts.colour AS `status_colour`, 
ts.active AS `active`, 
pa.name AS `pop_account_name` 
FROM 
tickets t 
LEFT JOIN users u ON u.id = t.user_id 
LEFT JOIN users u2 ON u2.id = t.assigned_user_id 
LEFT JOIN users u3 ON u3.id = t.submitted_user_id 
LEFT JOIN ticket_priorities tp ON tp.id = t.priority_id 
LEFT JOIN ticket_departments td ON td.id = t.department_id 
LEFT JOIN ticket_status ts ON ts.id = t.state_id 
LEFT JOIN pop_accounts pa ON pa.id = t.pop_account_id 
WHERE   
1 = 1 
AND t.site_id = :site_id 
AND ( 
    t.department_id IN (SELECT utd.department_id FROM users_to_departments utd WHERE utd.user_id = :department_or_assigned_or_user_id AND utd.site_id = :site_id)
OR 
    (t.assigned_user_id = :department_or_assigned_or_user_id OR t.user_id = :department_or_assigned_or_user_id)
)
ORDER BY 
t.last_modified DESC 
LIMIT :limit OFFSET :offset

这里是 MySQL Explain 结果的链接: http://michaeldale.com.au/images/explain.html

【问题讨论】:

  • mysql 命令的输出是什么:EXPLAIN your_query_here?
  • 感谢您的回复。我已经在帖子底部添加了一个链接到解释结果。

标签: mysql performance subquery


【解决方案1】:

enter code here如果你确定你的缓慢是由于引用的子查询,你的表 users_to_departments 应该有一个基于 user_id + site_id 的索引 字段。

此外,将 IN/NOT IN 或 EXISTS/NOT EXISTS 替换为 LEFT JOIN 并结合 NULL 或 NOT NULL(在 WHERE 子句上)对连接表的约束确实提高了 SQL Server 引擎的性能。

考虑到我对表格内容并不完全了解,我将向您提出两个建议(我您可以只使用选项 2 或将两者结合使用):

1 - 我会将当前查询拆分为两个查询并使用子句 UNION ALL 将它们连接到一个结果集中(通常我也使用子句 ALL重复可见,因为它们通常代表设计不良的查询或不充分的数据库设计)

2 - 我会尝试用 LEFT JOIN 替换 IN 子查询,以评估性能提升是否符合您的需要。请注意,由于子查询可能在 [department_id] 上包含重复项,因此您可能需要或不需要在您的选择之上使用 DISTINCT 子句。

SELECT 
DISTINCT 
t.* ,  
u.pushover_key AS `owner_pushover_key`,  
u.name AS `owner_name`,  
u.id AS `owner_id`,  
u.email AS `owner_email`,  
u.phone_number AS `owner_phone`,  
u.email_notifications AS `owner_email_notifications`,  
u2.pushover_key AS `assigned_pushover_key`,  
u2.name AS `assigned_name`,  
u2.id AS `assigned_id`,  
u2.email AS `assigned_email`,  
u2.email_notifications AS `assigned_email_notifications`,  
u3.name AS `submitted_name`,  
u3.id AS `submitted_id`,  
u3.email AS `submitted_email`,  
u3.email_notifications AS `submitted_email_notifications`,  
tp.name AS `priority_name`,  
td.name AS `department_name`,  
ts.name AS `status_name`,  
ts.colour AS `status_colour`,  
ts.active AS `active`,  
pa.name AS `pop_account_name`  
FROM  
tickets t  
LEFT JOIN users u ON u.id = t.user_id  
LEFT JOIN users u2 ON u2.id = t.assigned_user_id  
LEFT JOIN users u3 ON u3.id = t.submitted_user_id  
LEFT JOIN ticket_priorities tp ON tp.id = t.priority_id  
LEFT JOIN ticket_departments td ON td.id = t.department_id  
LEFT JOIN ticket_status ts ON ts.id = t.state_id  
LEFT JOIN pop_accounts pa ON pa.id = t.pop_account_id 
LEFT JOIN users_to_departments utd ON utd.department_id = t.department_id AND utd.user_id = :department_or_assigned_or_user_id AND utd.site_id = :site_id
WHERE    
1 = 1  
AND t.site_id = :site_id  
AND (
    utd.department_id NOT NULL
OR  
    (t.assigned_user_id = :department_or_assigned_or_user_id OR t.user_id = :department_or_assigned_or_user_id) 
) 
ORDER BY  
t.last_modified DESC  
LIMIT :limit OFFSET :offset 

希望对你有帮助

【讨论】:

  • 感谢您的回复。 users_to_departments 确实具有所有正确的索引。问题是子查询是一个依赖子查询。是的,我认为 JOIN 会更好,只是不确定如何获得我想要的结果。
  • 感谢您的更新。对不起,我错过了。看起来不错,我试试看。
  • 好的,所以选项 2 是最容易测试的,而且似乎没有更快。我怀疑 UNION ALL 将是要走的路。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-28
  • 1970-01-01
  • 2012-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多