什么效率更高,使用where子句或使用具有百万个以上行表的联接?


17

我们运行的网站在一个表中有250MM行,而在大多数查询中,与之连接的另一个表中的行都不到15MM。

样本结构:

MasterTable (Id, UserId, Created, Updated...) -- 15MM Rows
DetailsTable (Id, MasterId, SomeColumn...) -- 250MM Rows
UserTable (Id, Role, Created, UserName...) -- 12K Rows

我们通常必须对所有这些表进行一些查询。一种是获取免费用户(约1万个免费用户)的统计信息。

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

问题在于,由于联接早于where发生,因此该查询有时会运行很长的时间。

在这种情况下,使用wheres代替join还是可能更明智where column in(...)


1
什么数据库和版本?
Leigh Riffel

2
您是否尝试过两种方式?
gbn

如果是Oracle,我将在NVL2(Role,NULL,ID)上为UserTable创建一个基于函数的索引,但这看起来就像另一个数据库。
Leigh Riffel

Answers:


20

对于现代RDBMS,“显式JOIN”和“ JOIN-in-the-WHERE”(如果所有JOINS都是INNER)在性能和查询计划方面没有区别。

显式的JOIN语法更清晰,模棱两可(请参阅下面的链接)

现在,WHERE-before-WHERE是逻辑处理,而不是实际处理,现代的优化器足够聪明地实现这一点。

您的问题很可能是索引。

请向我们显示这些表上的所有索引和键。和查询计划

注意:这个问题现在已经在StackOverflow上关闭了,因为现在已经重复了……COUNT(1)vs COUNT(*)也是另一个神话。


2
joinwhere子句之间没有区别并不总是正确的。我一直在优化长时间运行的查询,有时using where子句的查询性能要比使用子句的查询好join70倍。如果那是那么简单明了,生活将是所有彩虹和独角兽。这是不是有些晦涩的古引擎-现在我期待在70X的优势where在2012年的SQL子句
ajeh

更进一步,我经常在两种方法中观察到完全相同的计划,并且隔离的查询执行的结果完全相同,但是当where子句查询在大批处理中运行时,它应该是其中的一部分,它在很大程度上优于join查询。SQL查询不能在真空中执行-它们会受到其余服务器有效负载的影响,并且where子句查询的费用通常会很好,这令人讨厌,因为join语法确实更简洁。
2015年

3
@ajeh:我建议您的经历是非常不典型的。您有疑问更大的问题,如果你有X70的区别:它就是这么简单
GBN

5

您必须完全重构查询

尝试先执行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;

即使您对这个重构查询运行了一个EXPLAIN计划,但是看起来比原来的查询看起来更糟,还是尝试一下。内部创建的临时表将执行笛卡尔联接,但这些表较小,无法使用。

我从这个YouTube视频中得到了这个主意

我在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;

如果NullRoles X 20 <AllRoles(换句话说,如果NullRoles小于表行的5%),则应在UserTable中创建一个非唯一索引Role。否则,由于查询优化器可能会排除使用索引,因此UserTable的完整表就足够了。

更新2011-06-25 12:40 EDT

由于我是MySQL DBA,因此我的处事方法不需要通过积极的悲观主义和保守态度来信任MySQL Query Optimizer。因此,我将尝试重构查询或创建必要的覆盖索引,以超越MySQL Query Optimizer的隐藏坏习惯。@gbn的答案似乎更完整,因为SQL Server可能对评估查询有更多的“心态”。


0

我们有一个[Detail]表,大约有7500万行;一个大约40万行的[主]表和一个相关的[Item]表,该表始终有7行。它存储了一小组“项目编号”(1-7),并且正在模拟纸质表格,每月打印和分发数百万。最快的查询是您最想不到的查询,涉及使用笛卡尔联接。IIRC,它类似于:

SELECT m.order_id, i.line_nr, d.Item_amt
FROM Master m, Item i 
INNER JOIN Detail d ON m.order_id = d.order_id

即使[Item]和[Detail]之间存在逻辑“ id”链接,但CROSS JOIN的效果比INNER JOIN更好。

RDBMS是Teradata及其MPP技术,而IDR是索引方案。7行表没有索引,因为TABLE SCAN始终表现最佳。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.