我注意到一些奇怪的行为:
运行此查询:
SELECT TOP 5000 t1.f1,t1.f2,t1.f3
FROM t1
JOIN t2 on t1.f1 = t2.f1
WHERE t2.f1 IS NOT NULL AND (t1.f5 != t2.f3)
在2秒内产生3447行。
运行这个:
SELECT t1.f1,t1.f2,t1.f3
FROM t1
JOIN t2 on t1.f1 = t2.f1
WHERE t2.f1 IS NOT NULL AND (t1.f5 != t2.f3)
永远运行直到我停止它(至少120分钟!!)。
表t1
和t2
包含大约500,000条记录。
我总是认为如果总行数低于该数字,则TOP
语句无关紧要,但是,似乎存在非常显着的差异。这是正常的(如果是这样,为什么)或者这只是一个侥幸?
修改
根据要求:
T1:
CREATE TABLE [dbo].[t1](
[f1] [int] NOT NULL,
[f2] [varchar](10) NULL,
[f3] [varchar](4) NULL,
[f4] [int] NOT NULL,
[f5] [varchar](max) NULL,
CONSTRAINT [PK_t1] PRIMARY KEY CLUSTERED
(
[f1] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
F2:
CREATE TABLE [dbo].[t2](
[f1] [nchar](10) NOT NULL,
[f2] [nchar](10) NOT NULL,
[f3] [varchar](max) NOT NULL,
[f4] [nchar](10) NULL,
[f5] [date] NULL,
[f6] [date] NULL,
[f7] [nchar](1) NULL,
CONSTRAINT [PK_t2] PRIMARY KEY CLUSTERED
(
[f1] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
执行计划:
有顶:
没有顶部:
看着这个我必须得出结论sorting
(为什么这样做?)导致延迟...你同意吗?
Edit2:根据要求,带有循环选项的执行计划没有顶部:
答案 0 :(得分:13)
问题是你的两个表[t1]和[t2]对于JOIN列f1
有完全不同的(并且很大程度上不兼容)数据类型。
这使得查询优化器无法准确估计这两个500,000行表之间要匹配的行数。它似乎使用默认的“猜测”,在这种情况下是实际数字的总 - 估计(3477)。因此,当你不使用TOP时,它认为排序然后合并行(O(NLogN))比执行嵌套循环(O(N ^ 2))更有效,因为它确实如此。没有意识到(合并)JOIN实际上会消除几乎所有的行。
当你打开TOP 5000时,它意识到嵌套循环更好,因为它将被切断不超过5000(远小于500k ^ 2,甚至小于500k * Log(500k)) 。但与嵌套循环不同,Merge-Sort不能以递增方式完成,它必须首先包含Sort的所有行。因此,将输出切断为5000,根本不会为您节省太多,因此使嵌套循环显然是更好的选择(即使JOIN估计错误)。
这里的根本问题是列T2.f1是NCHAR(10),对于看起来应该包含整数的东西来说,这是一个非常糟糕的选择。最好的解决方案是将该列的数据类型更改为INT。
如果由于某种原因你无法做到这一点,那么根据你的SQL Server版本,你可以通过添加一个持久的计算列来计算INT转换后的值[f1],然后抛出一个兼容的数据来结束运行上的索引。这将允许索引和统计数据再次用于此类查询。
作为最后的手段,您还可以使用查询提示。我通常不推荐它们,因为它们往往是后来导致问题的权宜之计解决方案。但是,如果您认为这是您唯一的选择,那么在查询结尾添加OPTION (FAST 1000)
可能会有效。
答案 1 :(得分:2)
可以通过多种不同方式优化SQL查询。两种常见方式是“最快的第一行”和“最快的最后一行”。也就是说,您是否希望最小化获得任何结果的时间或获得完整结果集的时间。
我猜这两个版本的优化程度不同。正如Aaron建议的那样,你可以通过查看执行计划来检查这一点。我通常的赌注是慢速版本使用嵌套循环连接。您可以使用优化程序提示解决此问题,例如:
<your query>
option (MERGE JOIN, HASH JOIN)
还有其他可能性。也许这些表正在更新,并且在运行第二个查询时,表恰好具有完整的表锁。您可以使用sp_who2
。