查询优化(多个连接)
Query optimization (multiple joins)
我想找到一种改进查询的方法,但似乎我已经完成了所有工作。让我告诉你一些细节。
下面是我的查询:
SELECT
`u`.`id` AS `id`,
`p`.`lastname` AS `lastname`,
`p`.`firstname` AS `firstname`,
COALESCE(`r`.`value`, 0) AS `rvalue`,
SUM(`rat`.`category` = 'A') AS `count_a`,
SUM(`rat`.`category` = 'B') AS `count_b`,
SUM(`rat`.`category` = 'C') AS `count_c`
FROM
`user` `u`
JOIN `user_customer` `uc` ON (`u`.`id` = `uc`.`user_id`)
JOIN `profile` `p` ON (`p`.`id` = `u`.`profile_id`)
JOIN `ad` FORCE INDEX (fk_ad_customer_idx) ON (`uc`.`customer_id` = `ad`.`customer_id`)
JOIN `ac` ON (`ac`.`id` = `ad`.`ac_id`)
JOIN `a` ON (`a`.`id` = `ac`.`a_id`)
JOIN `rat` ON (`rat`.`code` = `a`.`rat_code`)
LEFT JOIN `r` ON (`r`.`id` = `u`.`r_id`)
GROUP BY `u`.`id`
;
注意:一些 table 和列名是自愿隐藏的。
现在让我给你一些体积数据:
user => 6534 rows
user_customer => 12 923 rows
profile => 6511 rows
ad => 320 868 rows
ac => 4505 rows
a => 536 rows
rat => 6 rows
r => 3400 rows
最后,我的执行计划:
我的查询目前 运行 在大约 1.3 到 1.7 秒内完成,这当然很慢,足以惹恼我的应用程序的用户......此外,结果集由 165 行组成。
有什么方法可以改进吗?
谢谢。
编辑 1(对下面 Rick James 的回答):
不使用FORCE INDEX时速度和EXPLAIN是多少?
令人惊讶的是,当我不使用 FORCE INDEX 时它会变得更快。老实说,我真的不记得为什么我做了那个改变。在我的各种尝试中,我可能发现它在性能方面有更好的结果,并且从那以后就没有删除它。
当我不使用 FORCE INDEX 时,它使用另一个索引 ad_customer_ac_id_blocked_idx(customer_id, ac_id, blocked) 并且时间约为 1.1 秒。
我不太明白,因为 fk_ad_customer_idx(customer_id) 在我们谈论 customer_id.
上的索引时是一样的
首先,您不需要在查询中使用 tick
.everyTableAndColumn
,也不需要结果列、别名等。tick
标记主要用于您在与保留工作冲突,因此解析器知道您指的是特定列...就像 table 和一个名为 "JOIN" 的列一样,但 JOIN 是 SQL 命令的一部分。 . 看到它会造成的混乱。也有助于清晰的可读性。
接下来,这只是个人喜好,可以帮助您和您后面的其他人在数据和他们的关系上。我将连接显示为从它的来源缩进。正如您在下面看到的,我看到了如何从 User(u 别名)到 rat 别名 table 的链...您只能深入 5 个级别才能到达那里,我把第一个 table 在连接的左侧(来自 table)然后 = table 连接到连接的右侧。
现在,我可以看到关系,我建议如下。在具有条件的 table 上创建 COVERING 索引,并在适当的地方创建 id/value。这样查询就可以得到它所需要的最好的结果,来自索引页面的数据与必须转到原始数据。所以这里有索引的建议。
table index
user_customer ( user_id, customer_id ) -- dont know what your fk_ad_customer_idx parts are)
ad ( customer_id, ac_id )
ac ( id, a_id )
a (id, rat_code )
rat ( code, category )
重新格式化查询以提高可读性和查看 tables
之间的关系
SELECT
u.id,
p.lastname,
p.firstname,
COALESCE(r.value, 0) AS rvalue,
SUM(rat.category = 'A') AS count_a,
SUM(rat.category = 'B') AS count_b,
SUM(rat.category = 'C') AS count_c
FROM
user u
JOIN user_customer uc
ON u.id = uc.user_id
JOIN ad FORCE INDEX (fk_ad_customer_idx)
ON uc.customer_id = ad.customer_id
JOIN ac
ON ad.ac_id = ac.id
JOIN a
ON ac.a_id = a.id
JOIN rat
ON a.rat_code = rat.code
JOIN profile p
ON u.profile_id = p.id
LEFT JOIN r
ON u.r_id = r.id
GROUP BY
u.id
去掉FORCE INDEX
。即使昨天有用;明天可能会痛。
其中一些索引可能是有益的。 (很难预测;所以只需将它们全部相加即可。)
a: (rat_code, id)
rat: (code, category)
ac: (a_id, id)
ad: (ac_id, customer_id)
ad: (customer_id, ac_id)
uc: (customer_id, user_id)
uc: (user_id, customer_id)
u: (profile_id, r_id, id)
(假设id
是每个table的PRIMARY KEY
。注意none先有id
。)以上大部分是"covering".
另一种有时有用的方法:在加入任何不必要的 table 之前收集 SUMs
。但是似乎 p
是唯一不参与从 u
(GROUP BY
的目标)到 r
和 rat
(用于聚合)。它看起来像:
SELECT ..., firstname, lastname
FROM ( everything as above except for `p` ) AS most
JOIN `profile` `p` ON (`p`.`id` = most.`profile_id`)
GROUP BY most.id
这避免了在进行大多数连接和 GROUP BY
.
时绕过名字和姓氏
在执行 JOINs
和 GROUP BY
时,请务必对聚合进行完整性检查。您的 COUNTs
和 SUMs
可能 比应有的大。
我想找到一种改进查询的方法,但似乎我已经完成了所有工作。让我告诉你一些细节。
下面是我的查询:
SELECT
`u`.`id` AS `id`,
`p`.`lastname` AS `lastname`,
`p`.`firstname` AS `firstname`,
COALESCE(`r`.`value`, 0) AS `rvalue`,
SUM(`rat`.`category` = 'A') AS `count_a`,
SUM(`rat`.`category` = 'B') AS `count_b`,
SUM(`rat`.`category` = 'C') AS `count_c`
FROM
`user` `u`
JOIN `user_customer` `uc` ON (`u`.`id` = `uc`.`user_id`)
JOIN `profile` `p` ON (`p`.`id` = `u`.`profile_id`)
JOIN `ad` FORCE INDEX (fk_ad_customer_idx) ON (`uc`.`customer_id` = `ad`.`customer_id`)
JOIN `ac` ON (`ac`.`id` = `ad`.`ac_id`)
JOIN `a` ON (`a`.`id` = `ac`.`a_id`)
JOIN `rat` ON (`rat`.`code` = `a`.`rat_code`)
LEFT JOIN `r` ON (`r`.`id` = `u`.`r_id`)
GROUP BY `u`.`id`
;
注意:一些 table 和列名是自愿隐藏的。
现在让我给你一些体积数据:
user => 6534 rows
user_customer => 12 923 rows
profile => 6511 rows
ad => 320 868 rows
ac => 4505 rows
a => 536 rows
rat => 6 rows
r => 3400 rows
最后,我的执行计划:
我的查询目前 运行 在大约 1.3 到 1.7 秒内完成,这当然很慢,足以惹恼我的应用程序的用户......此外,结果集由 165 行组成。
有什么方法可以改进吗?
谢谢。
编辑 1(对下面 Rick James 的回答): 不使用FORCE INDEX时速度和EXPLAIN是多少?
令人惊讶的是,当我不使用 FORCE INDEX 时它会变得更快。老实说,我真的不记得为什么我做了那个改变。在我的各种尝试中,我可能发现它在性能方面有更好的结果,并且从那以后就没有删除它。
当我不使用 FORCE INDEX 时,它使用另一个索引 ad_customer_ac_id_blocked_idx(customer_id, ac_id, blocked) 并且时间约为 1.1 秒。 我不太明白,因为 fk_ad_customer_idx(customer_id) 在我们谈论 customer_id.
上的索引时是一样的首先,您不需要在查询中使用 tick
.everyTableAndColumn
,也不需要结果列、别名等。tick
标记主要用于您在与保留工作冲突,因此解析器知道您指的是特定列...就像 table 和一个名为 "JOIN" 的列一样,但 JOIN 是 SQL 命令的一部分。 . 看到它会造成的混乱。也有助于清晰的可读性。
接下来,这只是个人喜好,可以帮助您和您后面的其他人在数据和他们的关系上。我将连接显示为从它的来源缩进。正如您在下面看到的,我看到了如何从 User(u 别名)到 rat 别名 table 的链...您只能深入 5 个级别才能到达那里,我把第一个 table 在连接的左侧(来自 table)然后 = table 连接到连接的右侧。
现在,我可以看到关系,我建议如下。在具有条件的 table 上创建 COVERING 索引,并在适当的地方创建 id/value。这样查询就可以得到它所需要的最好的结果,来自索引页面的数据与必须转到原始数据。所以这里有索引的建议。
table index
user_customer ( user_id, customer_id ) -- dont know what your fk_ad_customer_idx parts are)
ad ( customer_id, ac_id )
ac ( id, a_id )
a (id, rat_code )
rat ( code, category )
重新格式化查询以提高可读性和查看 tables
之间的关系SELECT
u.id,
p.lastname,
p.firstname,
COALESCE(r.value, 0) AS rvalue,
SUM(rat.category = 'A') AS count_a,
SUM(rat.category = 'B') AS count_b,
SUM(rat.category = 'C') AS count_c
FROM
user u
JOIN user_customer uc
ON u.id = uc.user_id
JOIN ad FORCE INDEX (fk_ad_customer_idx)
ON uc.customer_id = ad.customer_id
JOIN ac
ON ad.ac_id = ac.id
JOIN a
ON ac.a_id = a.id
JOIN rat
ON a.rat_code = rat.code
JOIN profile p
ON u.profile_id = p.id
LEFT JOIN r
ON u.r_id = r.id
GROUP BY
u.id
去掉FORCE INDEX
。即使昨天有用;明天可能会痛。
其中一些索引可能是有益的。 (很难预测;所以只需将它们全部相加即可。)
a: (rat_code, id)
rat: (code, category)
ac: (a_id, id)
ad: (ac_id, customer_id)
ad: (customer_id, ac_id)
uc: (customer_id, user_id)
uc: (user_id, customer_id)
u: (profile_id, r_id, id)
(假设id
是每个table的PRIMARY KEY
。注意none先有id
。)以上大部分是"covering".
另一种有时有用的方法:在加入任何不必要的 table 之前收集 SUMs
。但是似乎 p
是唯一不参与从 u
(GROUP BY
的目标)到 r
和 rat
(用于聚合)。它看起来像:
SELECT ..., firstname, lastname
FROM ( everything as above except for `p` ) AS most
JOIN `profile` `p` ON (`p`.`id` = most.`profile_id`)
GROUP BY most.id
这避免了在进行大多数连接和 GROUP BY
.
在执行 JOINs
和 GROUP BY
时,请务必对聚合进行完整性检查。您的 COUNTs
和 SUMs
可能 比应有的大。