Rya*_*ard 5 mysql performance normalization join
我第一次遇到 MySQL 查询执行时间过长(约 5 分钟)的问题。
数据库中的数据是高度(而非任意)规范化的。它非常有效地组织和改组数据以用于不同目的的许多非常有用的方式显示,除了这个特定的查询正在向它投掷扳手。
我无法理解这样做的原因。但是,一些背景信息可能会对其他任意复杂的查询有所了解。
该公司将世界划分为许多团队(macroregions)。根据他们的专业知识,每个人都属于一两个团队。
例如,有许多不同的团队。几个例子是Spanish
、Sahara
、Iberia
、Portuguese
、Jungle
团队。每个团队都与其他团队有相当大的重叠,但在某种意义上是独立的。
该Arabic
团队与Sahara
团队密切合作,因为数据库告诉他们由于地理位置重叠,他们必须在某些任务上一起工作。在Spanish
和Portuguese
球队也紧密合作,他们与工作都Americas
与Europe
队和Portuguese
队还与Africa
球队一样,该Arabic
团队。
每个团队都有一组给定的区域,这些区域也不是该特定团队独有的。例如,该Mediterranean
地区属于大约 12 个团队,当那里发生事件时,他们都会一起工作。
每个国家属于一个或多个地区。Turkey
属于Central Asia
,Europe
甚至Mediterranean
,以及其他一些。
鉴于所有这些,有必要向每个人展示他们团队中的其他人正在做什么,以及不在他们的团队中但具有重叠区域的人。
查询 1完美地完成了这一点,而且非常快,不到 0.09 秒。
SELECT report_name
FROM reports
WHERE region IN (
SELECT distinct region
FROM macroregions
WHERE macroregion IN (
SELECT distinct macroregion
FROM users
WHERE callsign = '$thisuser'
)
)
Run Code Online (Sandbox Code Playgroud)
report_name
当查询 1 认为登录的个人应该知道它时,人们可以看到 的每次出现也很重要。Reports_names
为每个地理位置自动生成和重复使用
如果我在East Asia
团队中并且我所在地区的某人正在与Spanish
团队合作,则数据库中将有两个条目:
20120210JOMX01
Japan
Okinawa
20120210JOMX01
Mexico
Nuevo Leon
查询 2接受查询 1并WHEN-IN
在其周围包裹一层s。它让East Asia
团队成员知道他们团队中的某个人正在工作Mexico
。但是性能延迟是不能接受的,完全不能用;完成一个查询需要将近5分钟!
SELECT *
FROM reports
WHERE report_name IN (
SELECT report_name
FROM reports
WHERE region IN (
SELECT distinct region
FROM macroregions
WHERE macroregion IN (
SELECT distinct macroregion
FROM users
WHERE callsign = '$thisuser'
)
)
)
Run Code Online (Sandbox Code Playgroud)
尽管查询有效,但它需要很长时间才能生效。同样,这个特定查询需要多长时间令人吃惊。而界面中的其他任何东西都没有给我带来任何性能问题(即它们都需要不到一秒钟)。
我可以采取哪些步骤来解决这个问题?
让我们从您的原始查询开始
SELECT *
FROM reports
WHERE report_name IN (
SELECT report_name
FROM reports
WHERE region IN (
SELECT distinct region
FROM macroregions
WHERE macroregion IN (
SELECT distinct macroregion
FROM users
WHERE callsign = '$thisuser'
)
)
)
Run Code Online (Sandbox Code Playgroud)
您只能(分阶段)收集密钥,然后将密钥与reports
表连接起来。这是我新提出的查询
SELECT reports.* FROM
(
SELECT rpts.report_name FROM
(
SELECT DISTINCT regions.region FROM
(SELECT DISTINCT macroregion
FROM users WHERE callsign = '$thisuser') users
INNER JOIN
(SELECT DISTINCT macroregion,region FROM regions) regions
USING (macroregion)
) regionkeys
INNER JOIN
(SELECT region,report_name FROM reports) rpts
USING (region)
) reportnamekeys
INNER JOIN reports
USING (report_name);
Run Code Online (Sandbox Code Playgroud)
如果您对 USING 子句不满意,这里是我在不使用 USING 子句的情况下新提出的查询:
SELECT reports.* FROM
(
SELECT rpts.report_name FROM
(
SELECT DISTINCT regions.region FROM
(SELECT DISTINCT macroregion
FROM users WHERE callsign = '$thisuser') users
INNER JOIN
(SELECT DISTINCT macroregion,region FROM regions) regions
ON users.macroregion = regions.macroregion
) regionkeys
INNER JOIN
(SELECT region,report_name FROM reports) rpts
ON regionkeys.region = rpts.region
) reportnamekeys
INNER JOIN reports
ON reportkeys.report_name = reports.report_name;
Run Code Online (Sandbox Code Playgroud)
您将需要一些索引来支持子查询
ALTER TABLE users ADD INDEX callsign_macroregion_ndx (callsign,macroregion);
ALTER TABLE regions ADD INDEX macroregion_region_ndx (macroregion,region);
ALTER TABLE reports ADD INDEX region_report_name_ndx (region,report_name);
ALTER TABLE reports ADD INDEX report_name_ndx (report_name);
Run Code Online (Sandbox Code Playgroud)
前 3 个索引称为覆盖索引。之所以这样称呼它们,是因为子查询仅需要那些确切的列。因此,无需从表中读取。数据仅从索引中获取。
您还应该研究如何重构查询以收集键、尽早执行 WHERE 子句以及最后执行 JOIN。以下是有关如何执行此操作的精彩 YouTube 视频:http://youtu.be/ZVisY-fEoMw(基于本书:重构 SQL 应用程序)
试一试 !!!
归档时间: |
|
查看次数: |
1345 次 |
最近记录: |