Condizione del filtro non correttamente applicata all'indice Columnstore cluster


10

Usando l'esempio seguente, i predicati sono gli stessi, tuttavia l'istruzione superiore restituisce (correttamente) 0 righe, l'istruzione inferiore restituisce 1 - anche se i predicati NON corrispondono:

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

Perché potrebbe succedere?

Ulteriori informazioni:

  • L'indice non cluster nell'istruzione superiore NON viene filtrato
  • CheckDB restituisce 0 numeri
  • Versione server: Microsoft SQL Azure (RTM) - 12.0.2000.8 Dec 19 2018 08:43:17 Copyright (C) 2018 Microsoft Corporation

Incolla il link Plan:

https://www.brentozar.com/pastetheplan/?id=S1w_rU68E

Ulteriori informazioni:

Hanno funzionato dbcc checktable ([transaction]) with all_errormsgs, extended_logical_checks, data_purityche indica nessun problema.

Posso riprodurre in modo affidabile il problema su questa tabella durante il ripristino di un backup di questo database.


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Jack dice di provare topanswers.xyz il

Risposte:


7

Questo errore non richiede l'eliminazione o la ridenominazione delle colonne.

Vedrai anche lo stesso comportamento per il statusId = 100quale non è mai stato presente in nessuna versione della colonna.

Requisiti

  • Un archivio colonne raggruppato
  • Indice b-tree non cluster
  • Un piano che esegue una ricerca nel columnstore con
    • Righe di destinazione nell'archivio delta
    • Un predicato non SARG spinto
    • Un confronto con NULL usando un test di uguaglianza

Esempio

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;

Uno dei seguenti eviterà il bug:

  • Spostamento di righe dall'archivio delta utilizzando qualsiasi metodo inclusa la riorganizzazione con l'opzione compress rowgroups specificata
  • Scrivere il predicato per rifiutarlo esplicitamente = NULL
  • Abilitazione del flag di traccia non documentato 9130 per evitare di inserire il predicato nella ricerca

db <> demo violino .


Questo errore è stato corretto in CU15 per SQL Server 2017 (e CU7 per SQL Server 2016 SP2):

FIX: query su tabella con indice sia columnstore cluster che indice cluster non cluster potrebbero restituire risultati errati in SQL Server 2016 e 2017


8

Questo è un bug con SQL Server. Se una colonna viene eliminata da una tabella con un indice columnstore cluster e quindi viene aggiunta una nuova colonna con lo stesso nome, sembra utilizzare la vecchia colonna eliminata per il predicato. Ecco la MVCE:

Questo script inizia con 10000le righe con statusIddei 1e statusId2di 5- poi scende la statusIDcolonna e rinomina statusId2a statusId. Quindi alla fine tutte le righe dovrebbero avere un statusId5.

Ma la query seguente colpisce l'indice non cluster ...

select *
from example
where statusId = 1
    and total <= @filter
    and barcode = @barcode
    and id2 = @id2

... e restituisce 2righe (con il statusIddiverso selezionato da implicito dalla WHEREclausola) ...

+-------+---------+------+-------+----------+
|  id   | barcode | id2  | total | statusId |
+-------+---------+------+-------+----------+
|     5 |    5    | NULL |  5.00 |        5 |
| 10005 |    5    | NULL |  5.00 |        5 |
+-------+---------+------+-------+----------+

... mentre questo accede al columnstore e ritorna correttamente 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;

Ho anche sollevato un problema sul portale di feedback di Azure :

E per chiunque lo incontri, ricostruire il Clustered Columnstore Index risolve il problema:

alter index cci_example on example rebuild

La ricostruzione dell'ICC corregge solo i dati esistenti. Se vengono aggiunti nuovi record, il problema si ripresenta su questi record; quindi attualmente l'unica correzione nota per la tabella è ricrearla interamente.


1
Non solo il problema che sta usando quello vecchio per il predicato. L'altra cosa strana è che si rompe completamente il predicato residuo su diverse colonne and id2 = @id2dovrebbe garantire zero righe comunque come @id2è null, ma è ancora ottenere il 2
Martin Smith

RE: La tua modifica 2 fa REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);il lavoro? Ciò eliminerà il deltastore: il problema si verifica ancora per le nuove righe aggiunte successivamente?
Martin Smith,

No, sembra essere lo stesso esatto risultato purtroppo?
Uberzen1

-4

Sulla base dei piani, sembra che l'indice Columnstore sia stato creato con SET ANSI_NULLS OFF. Le tabelle e gli indici mantengono l'impostazione com'era al momento della creazione dell'indice. Puoi verificare creando un indice Columnstore duplicato assicurandoti che ANSI_NULLS sia ATTIVO, quindi rilasciando l'originale o disabilitandolo.

Ma, a meno che tu non abbia scoperto un bug di SQL Server, questo è l'unico modo in cui i risultati potrebbero verificarsi.


2
Sei sicuro che 1) gli indici non filtrati possano mantenere le impostazioni ANSI_NULLS separate dalla tabella di base e 2) che l'impostazione ANSI_NULLS della sessione possa effettivamente causare discrepanze quando la tabella viene creata con ANSI_NULLS OFF?
Forrest,

L'ho pensato, ma quando scrivo la definizione del CCI non ha opzioni impostate, e se lo creo con SET ANSI_NULLS ON prima della definizione dell'indice il risultato è lo stesso?
Uberzen1
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.