Стан фільтра неправильно застосовано до індексу кластерних стовпців


10

Використовуючи нижченаведений приклад, предикати є однаковими, однак верхній оператор (правильно) повертає 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

Чому це могло статися?

Додаткова інформація:

  • Індекс без кластеру у верхньому операторі НЕ фільтрується
  • CheckDB повертає 0 питань
  • Версія сервера: 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що свідчить про відсутність проблем.

Я можу надійно відтворити проблему проти цієї таблиці під час відновлення резервної копії цієї бази даних.


Коментарі не для розширеного обговорення; ця розмова була переміщена до чату .
Джек каже, спробуйте topanswers.xyz

Відповіді:


7

Ця помилка не потребує випадання або перейменування стовпців.

Ви також побачите ту саму поведінку, statusId = 100якої ніколи не було в жодній версії стовпця.

Вимоги

  • Кластерний стовпчик
  • Індекс некластеризованого b-дерева
  • План, який виконує пошук у магазині стовпців із
    • Цільові рядки в магазині дельти
    • Висунутий несказаний предикат
    • Порівняння з NULL за допомогою тесту рівності

Приклад

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
  • Увімкнення недокументованого прапора 9130 слід, щоб уникнути натискання присудка на пошук

db <> скриптова демонстрація.


Ця помилка була виправлена у CU15 для SQL Server 2017 (та CU7 для SQL Server 2016 SP2):

Виправлення: Запит на таблицю з кластерним індексом стовпців стовпців та некластеризованим індексом рядкових сховищ може повернути неправильні результати у SQL Server 2016 та 2017


8

Це помилка в SQL Server. Якщо стовпець видалено з таблиці з кластерним індексом стовпців стовпців, а потім додається новий стовпець з тим самим іменем, він, здається, використовує старий видалений стовпчик для предиката. Ось MVCE:

Цей сценарій починається з 10000рядками з statusIdз 1і statusId2з 5- потім опускає statusIDколону і перейменовує statusId2в statusId. Отже в кінці всіх рядів повинно бути statusId5.

Але наступний запит потрапляє на некластеризований індекс ...

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

Перебудова ІСН виправляє лише наявні дані. Якщо нові записи додаються, питання знову виникає на цих записах; тому наразі єдиним відомим виправленням таблиці є відтворення її повністю.


1
Не лише питання про те, що він використовує старий для присудка. Інша дивна річ в тому , що вона повністю розпадається залишковий предикат на різних стовпчиках and id2 = @id2повинні гарантувати нульові рядки в будь-якому випадку , як @id2це , nullале ви все одно отримаєте 2
Martin Smith

RE: Ваша редакція 2 робить REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);роботу? Це очистить дельтастор - чи все ще виникає проблема для нових рядків, доданих після цього?
Мартін Сміт

Ні, на жаль, точно такий же результат?
Uberzen1

-4

На основі планів виявляється, що індекс Columnstore був створений з SET ANSI_NULLS OFF. Таблиці та індекси зберігають налаштування, як це було під час створення індексу. Ви можете перевірити, створивши повторюваний індекс Columnstore, переконуючись, що ANSI_NULLS увімкнено, а потім відкиньте оригінал або відключивши його.

Але, якщо ви не виявили помилку на SQL Server, це може бути єдиним способом.


2
Ви впевнені, що 1) нефільтровані індекси можуть підтримувати параметри ANSI_NULLS окремо від базової таблиці, і 2) що налаштування ANSI_NULLS сеансу може насправді викликати розбіжності, коли таблиця створена з ANSI_NULLS OFF?
Форрест

Я думав про це, але коли я скриптую визначення CCI, у нього немає встановлених параметрів, і якщо я створюю його з SET ANSI_NULLS ON перед визначенням індексу, результат буде таким же?
Uberzen1
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.