La condición del filtro no se aplicó correctamente al índice de almacén de columnas en clúster

10

Usando el siguiente ejemplo, los predicados son los mismos, sin embargo, la declaración superior (correctamente) devuelve 0 filas, la declaración inferior devuelve 1, aunque los predicados NO coincidan:

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

¿Por qué podría estar pasando esto?

Informacion adicional:

  • El índice no agrupado en la declaración superior NO se filtra
  • CheckDB devuelve 0 problemas
  • Versión del servidor: Microsoft SQL Azure (RTM) - 12.0.2000.8 Dec 19 2018 08:43:17 Copyright (C) 2018 Microsoft Corporation

Pegue el enlace del plan:

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

Informacion adicional:

Han corrido lo dbcc checktable ([transaction]) with all_errormsgs, extended_logical_checks, data_purityque indica que no hay problemas.

Puedo reproducir de manera confiable el problema en esta tabla al restaurar una copia de seguridad de esta base de datos.

Uberzen1
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Jack dice que intente topanswers.xyz

Respuestas:

7

Este error no requiere soltar o renombrar columnas.

También verá el mismo comportamiento para el statusId = 100que nunca estuvo presente en ninguna versión de la columna.

Requisitos

  • Un almacén de columnas agrupado
  • Índice de árbol b no agrupado
  • Un plan que realiza una búsqueda en el almacén de columnas con
    • Fila (s) de destino en la tienda delta
    • Un predicado empujado no SARG
    • Una comparación con NULL usando una prueba de igualdad

Ejemplo

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;

Cualquiera de los siguientes evitará el error:

  • Mover filas fuera del almacén delta utilizando cualquier método, incluida la reorganización con la opción de compresión de grupos de filas especificada
  • Escribir el predicado para rechazar explícitamente = NULL
  • Habilitar el indicador de traza indocumentado 9130 para evitar empujar el predicado a la búsqueda

db <> demostración de violín .


Este error se corrigió en CU15 para SQL Server 2017 (y CU7 para SQL Server 2016 SP2):

REVISIÓN: la consulta en la tabla con el índice de almacén de columnas agrupado y el índice de almacén de filas no agrupado puede devolver resultados incorrectos en SQL Server 2016 y 2017

Paul White 9
fuente
8

Este es un error con SQL Server. Si se elimina una columna de una tabla con un índice de almacén de columnas en clúster, y luego se agrega una nueva columna con el mismo nombre, parece estar usando la antigua columna eliminada para el predicado. Aquí está el MVCE:

Este script comienza con 10000filas con statusIdof 1y statusId2of 5, luego suelta la statusIDcolumna y cambia el nombre statusId2a statusId. Entonces, al final, todas las filas deben tener un statusId5.

Pero la siguiente consulta golpea el índice no agrupado ...

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

... y devuelve 2filas (con el seleccionado statusIddiferente del implícito en la WHEREcláusula) ...

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

... mientras que este accede al almacén de columnas y devuelve correctamente 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;

También he planteado un problema en el portal de comentarios de Azure :

Y para cualquier otra persona que se encuentre con esto, la reconstrucción del índice de almacén de columnas en clúster soluciona el problema:

alter index cci_example on example rebuild

La reconstrucción de CCI solo corrige los datos existentes. Si se agregan nuevos registros, el problema surge nuevamente en estos registros; así que actualmente la única solución conocida para la tabla es recrearla por completo.

Uberzen1
fuente
1
No solo el problema de que está usando el anterior para el predicado. La otra cosa extraña es que rompe por completo el predicado residual sobre las diferentes columnas and id2 = @id2deben garantizar cero filas de todos modos como @id2es null, pero que aún así obtener el 2
Martin Smith
RE: ¿Tu edición 2 hace REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);el trabajo? Eso borrará el deltastore: ¿el problema aún ocurre para las nuevas filas agregadas después de eso?
Martin Smith
No, parece ser el mismo resultado tristemente?
Uberzen1
-4

Según los planes, parece que el índice Columnstore se creó con SET ANSI_NULLS OFF. Las tablas e índices conservan la configuración tal como era cuando se creó el índice. Puede verificarlo creando un índice de Columnstore duplicado mientras se asegura de que ANSI_NULLS esté ENCENDIDO, luego descarte el original o desactívelo.

Pero, a menos que haya descubierto un error de SQL Server, esta es la única forma en que los resultados podrían ocurrir.

Riendo Vergil
fuente
2
¿Está seguro de que 1) los índices no filtrados pueden mantener la configuración ANSI_NULLS separada de la tabla base y 2) que la configuración ANSI_NULLS de la sesión puede causar discrepancias cuando la tabla se crea con ANSI_NULLS OFF?
Forrest
Pensé esto, pero cuando escribo la definición del CCI no tiene opciones establecidas, y si lo creo con SET ANSI_NULLS ON antes de la definición del índice, ¿el resultado es el mismo?
Uberzen1