Ube*_*en1 10 sql-server azure-sql-database columnstore
使用下面的示例,谓词是相同的,但是顶部语句(正确)返回 0 行,底部语句返回 1 - 即使谓词不匹配:
declare @barcode nchar(22)=N'RECB012ZUKI449M1VBJZ'
declare @tableId int = null
declare @total decimal(10, 2) = 5.17
SELECT 1
FROM
[dbo].[transaction] WITH (INDEX([IX_Transaction_TransactionID_PaymentStatus_DeviceID_DateTime_All]))
WHERE
Barcode = @barcode
AND StatusID = 1
AND TableID = @tableID
AND @total <= Total
SELECT 1
FROM
[dbo].[transaction]
WHERE
Barcode = @barcode
AND StatusID = 1
AND TableID = @tableID
AND @total <= Total
Run Code Online (Sandbox Code Playgroud)
为什么会发生这种情况?
更多信息:
Microsoft SQL Azure (RTM) - 12.0.2000.8 Dec 19 2018 08:43:17 Copyright (C) 2018 Microsoft Corporation粘贴计划链接:
https://www.brentozar.com/pastetheplan/?id=S1w_rU68E
更多信息:
已经运行dbcc checktable ([transaction]) with all_errormsgs, extended_logical_checks, data_purity表明没有问题。
还原此数据库的备份时,我可以可靠地针对此表重现该问题。
这是 SQL Server 的一个错误。如果从具有聚集列存储索引的表中删除一列,然后添加具有相同名称的新列,则它似乎将旧的已删除列用作谓词。这是MVCE:
此脚本从10000带有statusIdof1和statusId2of 的行开始5- 然后删除该statusID列并重命名statusId2为statusId. 所以最后所有行的 a 都应该statusId是 5。
但是以下查询命中了非聚集索引...
select *
from example
where statusId = 1
and total <= @filter
and barcode = @barcode
and id2 = @id2
Run Code Online (Sandbox Code Playgroud)
...并返回2行(选定的statusId不同于WHERE子句所暗示的)...
+-------+---------+------+-------+----------+
| id | barcode | id2 | total | statusId |
+-------+---------+------+-------+----------+
| 5 | 5 | NULL | 5.00 | 5 |
| 10005 | 5 | NULL | 5.00 | 5 |
+-------+---------+------+-------+----------+
Run Code Online (Sandbox Code Playgroud)
... 而这个访问列存储并正确返回 0
select count(*)
from example
where statusId = 1
Run Code Online (Sandbox Code Playgroud)
MVCE
/*Create table with clustered columnstore and non clustered rowstore*/
CREATE TABLE example
(
id INT IDENTITY(1, 1),
barcode CHAR(22),
id2 INT,
total DECIMAL(10,2),
statusId TINYINT,
statusId2 TINYINT,
INDEX cci_example CLUSTERED COLUMNSTORE,
INDEX ix_example (barcode, total)
);
/* Insert 10000 rows all with (statusId,statusId2) = (1,5) */
INSERT example
(barcode,
id2,
total,
statusId,
statusId2)
SELECT TOP (10000) barcode = row_number() OVER (ORDER BY @@spid),
id2 = NULL,
total = row_number() OVER (ORDER BY @@spid),
statusId = 1,
statusId2 = 5
FROM sys.all_columns c1, sys.all_columns c2;
ALTER TABLE example
DROP COLUMN statusid
/* Now have 10000 rows with statusId2 = 5 */
EXEC sys.sp_rename
@objname = N'dbo.example.statusId2',
@newname = 'statusId',
@objtype = 'COLUMN';
/* Now have 10000 rows with StatusID = 5 */
INSERT example
(barcode,
id2,
total,
statusId)
SELECT TOP (10000) barcode = row_number() OVER (ORDER BY @@spid),
id2 = NULL,
total = row_number() OVER (ORDER BY @@spid),
statusId = 5
FROM sys.all_columns c1, sys.all_columns c2;
/* Now have 20000 rows with StatusID = 5 */
DECLARE @filter DECIMAL = 5,
@barcode CHAR(22) = '5',
@id2 INT = NULL;
/*This returns 2 rows from the NCI*/
SELECT *
FROM example WITH (INDEX = ix_example)
WHERE statusId = 1
AND total <= @filter
AND barcode = @barcode
AND id2 = @id2;
/*This counts 0 rows from the Columnstore*/
SELECT COUNT(*)
FROM example
WHERE statusId = 1;
Run Code Online (Sandbox Code Playgroud)
对于遇到此问题的任何其他人,重建聚集列存储索引可以解决问题:
alter index cci_example on example rebuild
Run Code Online (Sandbox Code Playgroud)
重建 CCI 只会修复任何现有数据。如果添加新记录,这些记录又会出现问题;所以目前该表唯一已知的修复方法是完全重新创建它。
此错误不需要删除或重命名列。
您还将看到statusId = 100在列的任何版本中从未出现过的相同行为。
DROP TABLE IF EXISTS dbo.Example;
GO
CREATE TABLE dbo.Example
(
c1 integer NOT NULL,
c2 integer NULL,
INDEX CCS CLUSTERED COLUMNSTORE,
INDEX IX NONCLUSTERED (c1)
);
GO
INSERT dbo.Example
(c1, c2)
VALUES
(1, NULL);
GO
DECLARE @c2 integer = NULL;
-- Returns one row but should not
SELECT
E.*
FROM dbo.Example AS E
WITH (INDEX(IX))
WHERE
E.c2 = @c2;
Run Code Online (Sandbox Code Playgroud)
以下任何一项都可以避免该错误:
= NULLdb<>小提琴演示。
此bug已被固定在CU15为SQL Server 2017年(和CU7为SQL Server 2016 SP2):
修复:对具有聚集列存储索引和非聚集行存储索引的表进行查询可能会在 SQL Server 2016 和 2017 中返回不正确的结果
| 归档时间: |
|
| 查看次数: |
583 次 |
| 最近记录: |