转换为数字时奇怪的 SQL Server 实例崩溃

Ste*_*Loe 20 sql-server sql-server-2008-r2 crash

在使用 C# Entity Framework 时,我注意到我的 SQL Server 实例崩溃了。

我能够将其追溯到以下声明:

SELECT * FROM dbo.[TestTable]
where mpnr in (1099059904,
1038139906,
1048119902,
1045119902,
1002109903,
1117109910,
1111149902,
1063149902,
1117159902,
1116109904,
1105079905,
1012079906,
1129129904,
1103059905,
1065059905,
1091059906,
1110149904,
1129149903,
1083029905,
1080139904,
1076109903,
1010019902,
1058019902,
1060019903,
1053019902,
1030089902,
1018149902,
1077149902,
1010109901,
1011109901,
1000119902,
1023049903,
1107119909,
1108119909,
1106119909)
Run Code Online (Sandbox Code Playgroud)

该表如下所示:

CREATE TABLE dbo.[TestTable]([MPNR] [numeric](9, 0) NOT NULL)
Run Code Online (Sandbox Code Playgroud)

每次启动查询时都会发生崩溃。如果我减少IN子句中值的数量,它就会起作用。(当然,它不返回任何行。)

我知道IN子句中的值是 10 位数字,列只有 9 位数字,但这不应导致整个 SQL Server 实例崩溃。

我的 SQL Server 的版本是 Windows Server 2003 32 位上的 2008 R2。

这是一个已知的错误?SQL Server 有补丁吗?

Mar*_*ith 20

我能够在 2008 R1 SP3 10.00.5512 上重现,但安装最新的 CU (14) 修复了它。

查看中间版本中修复的错误,您似乎需要升级到包含以下修复的版本。

在 SQL Server 2008 或 SQL Server 2012 中的 IN 子句中运行包含许多常量值的查询时出现访问冲突

由于您使用的是 2008 R2,SP1 至少需要 CU 9,SP2 至少需要 CU 5。

症状描述有些简短,但提到了不匹配的数据类型

当您运行在 Microsoft SQL Server 2008、Microsoft SQL Server 2012 或 Microsoft SQL Server 2008 R2 中的 IN 子句中包含许多常量值的查询时,可能会发生访问冲突。

注意 要发生此问题,IN 子句中的常量不能与列数据类型完全匹配。

它没有定义“许多”。从我所做的测试中,我怀疑这可能意味着“20 或更多”,因为这似乎是两种不同的基数估计方法之间的分界点。

崩溃发生在几个名为 by 的方法中CScaOp_In::FCalcSelectivity(),例如LoadHistogramFromXVariantArray()and CInMemHistogram::FJoin() -> WalkHistograms()

对于 19 个或更少的不同列表项,这些方法根本没有被调用。一个类似的 SQL Sever 2000 错误也提到这个截止点很重要。

用 100,000 行随机测试数据填充测试表,其值在 0 到 1047 之间,直方图开始如下

+--------------+------------+---------+---------------------+----------------+
| RANGE_HI_KEY | RANGE_ROWS | EQ_ROWS | DISTINCT_RANGE_ROWS | AVG_RANGE_ROWS |
+--------------+------------+---------+---------------------+----------------+
|            0 |          0 |     104 |                   0 | 1              |
|            8 |        672 |     118 |                   7 | 96             |
|           13 |        350 |     118 |                   4 | 87.5           |
|           18 |        395 |     107 |                   4 | 98.75          |
|           23 |        384 |      86 |                   4 | 96             |
|           28 |        371 |      85 |                   4 | 92.75          |
+--------------+------------+---------+---------------------+----------------+
Run Code Online (Sandbox Code Playgroud)

查询

SELECT * FROM dbo.[TestTable]
where mpnr in (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19)
option (maxdop 1)
Run Code Online (Sandbox Code Playgroud)

显示估计的 1856 行。

这正是通过分别获取 19 个等式谓词的估计行并将它们加在一起所期望的结果。

+-------+----------------+-------+
| 1-7   | AVG_RANGE_ROWS | 96    |
| 8     | EQ_ROWS        | 118   |
| 9-12  | AVG_RANGE_ROWS | 87.5  |
| 13    | EQ_ROWS        | 118   |
| 14-17 | AVG_RANGE_ROWS | 98.75 |
| 18    | EQ_ROWS        | 107   |
| 19    | AVG_RANGE_ROWS | 96    |
+-------+----------------+-------+

7*96 + 118 + 4*87.5 + 118 + 4*98.75 + 107 + 1*96 = 1856
Run Code Online (Sandbox Code Playgroud)

该公式在20添加到 in 列表后不再有效(估计行1902.75而不是1952将另一个添加96到总数中会生成的行)。

BETWEEN 似乎使用另一种计算基数估计的方法。

where mpnr BETWEEN 1 AND 20估计只有 1829.6 行。我不知道这是如何从显示的直方图得出的。