以下の例を使用すると、述語は同じですが、上のステートメントは(正しく)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
_
なぜこれが起こっているのでしょうか?
詳細情報:
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
を実行しました。
このデータベースのバックアップを復元するときに、このテーブルに対する問題を確実に再現できます。
このバグでは、列の削除や名前の変更は必要ありません。
また、どのバージョンの列にも存在しなかった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;
次のいずれかでバグを回避できます。
= NULL
db <> fiddle デモ。
このバグはfixedSQL Server 2017のCU15(およびSQL Server 2016 SP2のCU7)でした:
FIX:クラスター化列ストアインデックスと非クラスター化行ストアインデックスの両方を持つテーブルに対するクエリは、SQL Server 2016および2017で誤った結果を返す可能性があります
これはSQL Serverのバグです。クラスター化された列ストアインデックスを持つテーブルから列が削除され、同じ名前で新しい列が追加された場合、古い削除済み列が述語に使用されているように見えます。 MVCEは次のとおりです。
このスクリプトは、10000
のstatusId
と1
のstatusId2
を含む5
行で始まります。次に、statusID
列を削除し、statusId2
をstatusId
に名前変更します。したがって、最後にすべての行のstatusId
が5になるはずです。
しかし、次のクエリは非クラスター化インデックスにヒットします...
select *
from example
where statusId = 1
and total <= @filter
and barcode = @barcode
and id2 = @id2
... 2
行を返します(選択されたstatusId
は、WHERE
句で暗黙的に指定されたものとは異なります)...
+-------+---------+------+-------+----------+
| id | barcode | id2 | total | statusId |
+-------+---------+------+-------+----------+
| 5 | 5 | NULL | 5.00 | 5 |
| 10005 | 5 | NULL | 5.00 | 5 |
+-------+---------+------+-------+----------+
...これは列ストアにアクセスして正しく0
を返します
select count(*)
from example
where statusId = 1
[〜#〜] 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;
私も Azureフィードバックポータルで問題を提起しました :
そして、これに遭遇した人のために、クラスター化列ストアインデックスを再構築すると問題が修正されます。
alter index cci_example on example rebuild
CCIを再構築すると、既存のデータのみが修正されます。新しいレコードが追加された場合、問題はこれらのレコードで再び発生します。そのため、現在のところ、テーブルの唯一の既知の修正は、テーブルを完全に再作成することです。