Zap*_*002 6 performance sql-server statistics sql-server-2012 cardinality-estimates query-performance
我有一个表dbo.ClaimBilling,有 130,000 行。在该表中,列OperatorID是 avarchar(max)并且严重倾斜。125,000 行是 'user1',其余 5000 行分为 6 个其他值,'user2' 共有 3 条记录。
上有一个非聚集索引OperatorID,聚集索引是主键,IDClaimBilling。
我目前有以下查询:
SELECT DISTINCT IDClaimBilling
FROM dbo.ClaimBilling cb
INNER JOIN dbo.BillingItem bi
ON cb.IDClaimBilling = bi.ClaimID
WHERE OperatorID = @operator
Run Code Online (Sandbox Code Playgroud)
不管是什么值@operator,来自行的估计ClaimBilling都是~4000,这与任何值会返回的值都不接近,而且它始终是聚集索引扫描,它不使用operatorID索引。如果我删除加入并做
SELECT DISTINCT IDClaimBilling
FROM dbo.ClaimBilling
WHERE OperatorID = @operator
Run Code Online (Sandbox Code Playgroud)
然后它确实使用了OperatorID索引,但是无论 的值如何,估计都是错误的@operator,这次总是估计 ~18,000 左右。
我UPDATE STATISTICS dbo.ClaimBilling WITH FULLSCAN在运行查询之前做了一个。
即使统计信息确切地知道每个值有多少行,为什么这些估计值如此错误?
我@operator在测试中声明并分配一个值。它最初是程序的一部分,我认为这就是问题所在,但在临时声明中使用时它的行为也是一样的。
该查询仅在用户首次登录时运行,因此每个用户每天可能只运行几次。
根据问题留下的评论生成的社区 Wiki 答案
如果您将查询作为@operator变量运行,则 SQL Server 无法“嗅探”变量中的值,因此它将使用统计信息的平均密度值来计算估计值。无论您为变量分配什么值,该平均值估计值始终是相同的。
解决此问题的一种方法是使用OPTION (RECOMPILE)查询提示。这将在每次执行时编译一个新的计划,该计划针对当时变量中的特定值进行了优化。这是以每次语句重新编译为代价的(通常很小)。
您还可以模块化代码。您可以使用一条IF语句并检查 的值operatorid,如果它是“user1”,则调用一个存储过程,比方说sp_user1。如果不是“user1”,则调用不同的过程。第一个 sp 将针对“user1”进行优化,另一个 sp 将针对其余值进行优化。如果需要,您还可以option (recompile)在第二个 sp 中使用非“user1”值。
它也可能是动态 SQL 的一个不错的用例。这会将您的@operator变量转换为文字值,并为每个用户定制计划。由于该表中只有 7 个用户,我认为这不会给您带来真正的问题。
有关更多信息,请参阅:
| 归档时间: |
|
| 查看次数: |
610 次 |
| 最近记录: |