Jer*_*oyd 22 join best-practices
我们运行的网站在一个表中具有 250 MM 的行,而在另一个表中,我们将其连接到大多数查询中的行不到 15 MM。
示例结构:
MasterTable (Id, UserId, Created, Updated...) -- 15MM Rows
DetailsTable (Id, MasterId, SomeColumn...) -- 250MM Rows
UserTable (Id, Role, Created, UserName...) -- 12K Rows
Run Code Online (Sandbox Code Playgroud)
我们必须定期对所有这些表进行一些查询。一种是抓取免费用户(~10k 免费用户)的统计数据。
Select Count(1) from DetailsTable dt
join MasterTable mt on mt.Id = dt.MasterId
join UserTable ut on ut.Id = mt.UserId
where ut.Role is null and mt.created between @date1 and @date2
Run Code Online (Sandbox Code Playgroud)
问题是这个查询有时会运行很长时间,因为连接发生在 where 之前很久。
在这种情况下,使用 wheres 而不是 joins 或可能更明智where column in(...)吗?
gbn*_*gbn 22
对于现代 RDBMS,在性能和查询计划方面,“显式 JOIN”和“JOIN-in-the-WHERE”(如果所有 JOINS 都是 INNER)之间没有区别。
显式 JOIN 语法更清晰、更明确(见下面的链接)
现在,JOIN-before-WHERE 是逻辑处理而不是实际处理,现代优化器足够聪明来实现这一点。
您的问题很可能是索引。
请向我们展示这些表上的所有索引和键。和查询计划
注意:这个问题在 StackOverflow 上已经很接近了,因为它现在是重复的...... COUNT(1) vs COUNT(*) 也是另一个破灭的神话。
您必须完全重构查询
尝试先执行 WHERE 子句,然后再执行 JOIN
Select Count(1) from DetailsTable dt
join (Select UserId,Id FROM MasterTable where
created between @date1 and @date2) mt on mt.Id = dt.MasterId
join (Select Id FROM UserTable WHERE Role is NULL) ut
on ut.Id = mt.UserId;
Run Code Online (Sandbox Code Playgroud)
即使你在这个重构的查询上运行了一个 EXPLAIN 计划并且它看起来比你原来的更糟糕,无论如何都要尝试它。内部创建的临时表将执行笛卡尔连接,但这些表较小,无法使用。
我在 StackOverflow 的一个非常复杂的问题中尝试了视频中的原则,并获得了 200 分的奖励。
@gbn 提到确保您拥有正确的索引。在这种情况下,请在 MasterTable 中索引创建的列。
试一试 !!!
更新 2011-06-24 22:31 EDT
您应该运行这些查询:
SELECT COUNT(1) AllRoles FROM UserTable;
SELECT COUNT(1) NullRoles FROM UserTable WHERE Role is NULL;
Run Code Online (Sandbox Code Playgroud)
如果 NullRoles X 20 < AllRoles(换言之,如果 NullRoles 小于表行的 5%),您应该在 UserTable 中创建一个非唯一索引 Role。否则,一个完整的 UserTable 表就足够了,因为查询优化器可能会排除使用索引。
更新 2011-06-25 12:40 EDT
由于我是 MySQL DBA,我的做事方法要求不要通过积极的悲观和保守来相信 MySQL 查询优化器。因此,我将尝试重构查询或创建必要的覆盖索引,以克服 MySQL 查询优化器隐藏的坏习惯。@gbn 的答案似乎更完整,因为 SQL Server 可能对评估查询有更多的“头脑清醒”。
| 归档时间: |
|
| 查看次数: |
114967 次 |
| 最近记录: |