我认识到一种奇怪的行为。鉴于以下查询:
SELECT * FROM foo
JOIN bar ON bar.id = foo.bar_id AND bar.other_id = '00000000-0000-0000-0000-000000000000'
我的服务器上的执行时间:~120 ms,读取:~125000
当我像这样重写查询时:
DECLARE @other_id uniqueidentifier = '00000000-0000-0000-0000-000000000000'
SELECT * FROM foo
JOIN bar ON bar.id = foo.bar_id AND bar.other_id = @other_id
执行时间:~6ms,读取:~140
任何人都可以解释为什么第一个查询要慢得多吗?我假设GUID字符串在查询中反复转换,但我希望SQL服务器足够聪明,不能这样做。
答案 0 :(得分:3)
差异的可能原因是不同的执行计划,而不是从字符串重复转换。更高的读数表明了这一点。比较要验证的计划。
通常,在编译期间知道实际值并生成良好计划时,SQL Server将更准确地估计行计数。对于变量,估计值基于平均值而不是统计量直方图。尝试更新统计信息并再次运行第一个查询,因为这可能是陈旧统计信息的症状。
答案 1 :(得分:1)
我设法重现了你的问题。而Dan Guzman(+1)则排在正确的位置。以下是对正在进行的演示的演示:
使用以下代码创建表格和数据集:
CREATE TABLE dbo.Test(ID INT IDENTITY (1,1),Val UNIQUEIDENTIFIER)
GO
INSERT INTO dbo.Test
SELECT NEWID() FROM sys.columns
GO 30
INSERT INTO dbo.Test
SELECT TOP 1000 Val FROM dbo.Test
GO 30
CREATE UNIQUE CLUSTERED INDEX idx ON dbo.Test(ID)
CREATE NONCLUSTERED INDEX idx2 ON dbo.Test(Val)
GO
现在看看你的统计数据:
DBCC SHOW_STATISTICS ('dbo.test',IDX2)
在EQ_ROWS中,您将看到整数(与Hi键匹配的重复数),在AVG_RANGE_ROWS中,您将看到小数平均值(每个不同键的平均行数)
运行下一个语句以识别重复或不重复的密钥。
SELECT Val, COUNT(*) FROM dbo.Test GROUP BY Val HAVING COUNT(*) = 1
SELECT Val, COUNT(*) FROM dbo.Test GROUP BY Val HAVING COUNT(*) > 1
如果使用非唯一[Val]运行以下代码,则执行计划将完全相同,因为SQL会根据AVG_RANGE_ROWS生成计划。
如果运行带有Unique [Val]的代码,那么计划略有不同,估计的行数会发生变化,带参数的查询将使用AVG_RANGE_ROWS,带有硬编码值的查询将使用EQ_ROWS。这种差异可以使优化者在更复杂的环境中制定完全不同的计划。
DECLARE @r UNIQUEIDENTIFIER = 'CE043987-62B5-4AA6-9BE7-0005F2B54A24'
SELECT * FROM dbo.Test WHERE Val = @r
SELECT * FROM dbo.Test WHERE Val = 'CE043987-62B5-4AA6-9BE7-0005F2B54A24'
答案 2 :(得分:-1)
第一个选择首先执行JOIN,其中可能找到许多与JOIN条件匹配的行。 AND之后的第二个条件然后将此结果限制为一行。因此,几乎完整地读取了两个表格。
第二个选择具有两个条件,寻找定义为变量的单个值常量。如果每个表的id是一个或两个表的主键,这将特别快。