添加datalength条件会使查询变慢

时间:2012-12-06 11:14:26

标签: sql-server performance sql-server-2012 datalength

我有一个表格mytable,包括列datekeydate并且有索引),列contentsvarbinary(max),以及stringhashvarchar(100)stringhashdatekey一起构成了表的主键。一切都在我的本地机器上运行。

运行

SELECT TOP 1 * FROM mytable where datekey='2012-12-05'

返回0行并需要0秒。 但是如果我添加datalength条件:

SELECT TOP 1 * FROM mytable where datekey='2012-12-05' and datalength(contents)=0

它会运行很长时间,在我放弃等待之前不会返回任何内容。

我的问题: 为什么?我怎么知道为什么这么长时间?


这是我到目前为止所检查的内容:

当我点击“显示估计执行计划”时,它还需要很长时间,并且在我放弃等待之前不会返回任何内容。

如果我这样做

SELECT TOP 1000 datalength(contents) FROM mytable order by datalength(contents) desc

需要7秒钟并返回列表4228081,4218689等。

exec sp_spaceused 'mytable'

返回

rows        reserved     data         index_size  unused
564019      50755752 KB  50705672 KB  42928 KB    7152 KB

因此表格非常大,为50 GB。 运行

SELECT TOP 1000 * FROM mytable

需要26秒。

sqlservr.exe进程大约为6 GB,这是我为数据库设置的限制。

3 个答案:

答案 0 :(得分:4)

这需要很长时间,因为您的查询需要为每一行计算DATALENGTH,然后在返回第一条记录之前对结果进行排序。 如果字段的DATALENGTH(或者它是否包含任何值)是您可能重复查询的内容,我建议保留结果的另一个索引字段(可能是一个持久的计算字段),并搜索该字段。

答案 1 :(得分:1)

这个旧的msdn blog post似乎同意@MartW的答案,即每行都会评估datalength。但是理解“评估”的真正含义以及性能下降的真正根源是很好的。

正如问题中所提到的,contents列中每个值的大小可能很大。这意味着每个大于〜8Kb的值都存储在特殊的LOB存储中。因此,考虑到其他列的大小,很明显表中占用的大部分空间都是由这个LOB存储器占用的,即大约50Gb。

即使已经评估了每行的contents列的长度,这在上面的帖子链接中得到证明,它仍然存储在LOB中。因此引擎仍然需要读取LOB存储的某些部分来执行查询。

如果在执行查询时LOB存储不在RAM中,那么我们需要从磁盘读取它,这当然比从RAM慢得多。也可能读取LOB部分比线性更随机,这更加缓慢,因为它往往会增加需要从磁盘读取的整个内存块数量。

答案 2 :(得分:0)

目前它可能不会使用主键,因为在datekey列之前包含了stringhash列。尝试添加仅包含datekey列的其他索引。一旦创建了该密钥,如果它仍然很慢,您还可以尝试查询提示,例如:

SELECT TOP 1 * FROM mytable where datekey='2012-12-05' and datalength(contents)=0 WITH INDEX = IX_datekey

您还可以创建一个单独的长度列,该列在您的应用程序或插入/更新触发器中更新。