我有一个表格mytable,包括列datekey
(date
并且有索引),列contents
,varbinary(max)
,以及stringhash
列varchar(100)
。 stringhash
和datekey
一起构成了表的主键。一切都在我的本地机器上运行。
运行
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,这是我为数据库设置的限制。
答案 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
您还可以创建一个单独的长度列,该列在您的应用程序或插入/更新触发器中更新。