我如何有效地处理非常倾斜的数据?最新的统计数据,但似乎没有帮助

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在测试中声明并分配一个值。它最初是程序的一部分,我认为这就是问题所在,但在临时声明中使用时它的行为也是一样的。

该查询仅在用户首次登录时运行,因此每个用户每天可能只运行几次。

Pau*_*ite 2

根据问题留下的评论生成的社区 Wiki 答案

如果您将查询作为@operator变量运行,则 SQL Server 无法“嗅探”变量中的值,因此它将使用统计信息的平均密度值来计算估计值。无论您为变量分配什么值,该平均值估计值始终是相同的。

解决此问题的一种方法是使用OPTION (RECOMPILE)查询提示。这将在每次执行时编译一个新的计划,该计划针对当时变量中的特定值进行了优化。这是以每次语句重新编译为代价的(通常很小)。

您还可以模块化代码。您可以使用一条IF语句并检查 的值operatorid,如果它是“user1”,则调用一个存储过程,比方说sp_user1。如果不是“user1”,则调用不同的过程。第一个 sp 将针对“user1”进行优化,另一个 sp 将针对其余值进行优化。如果需要,您还可以option (recompile)在第二个 sp 中使用非“user1”值。

它也可能是动态 SQL 的一个不错的用例。这会将您的@operator变量转换为文字值,并为每个用户定制计划。由于该表中只有 7 个用户,我认为这不会给您带来真正的问题。

有关更多信息,请参阅: