เงื่อนไขการกรองไม่ถูกต้องนำไปใช้กับดัชนีคอลัมน์ที่เก็บแบบคลัสเตอร์


10

จากตัวอย่างด้านล่างเพรดิเคตจะเหมือนกันอย่างไรก็ตามคำสั่ง top (ถูกต้อง) ส่งคืน 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

วางลิงก์ Plan:

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-tree ที่ไม่คลัสเตอร์
  • แผนที่ดำเนินการค้นหาบน columnstore ด้วย
    • แถวเป้าหมายในที่จัดเก็บเดลต้า
    • เพรดิเคตที่ไม่ใช่ SARG ที่พุช
    • การเปรียบเทียบกับ 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):

การแก้ไข: แบบสอบถามกับตารางที่มีทั้งดัชนี columnstore คลัสเตอร์และดัชนี rowstore nonclustered อาจส่งคืนผลลัพธ์ที่ไม่ถูกต้องใน SQL Server 2016 และ 2017


8

นี่เป็นข้อผิดพลาดกับ SQL Server หากคอลัมน์ถูกลบออกจากตารางที่มีดัชนี columnstore แบบคลัสเตอร์จากนั้นคอลัมน์ใหม่จะถูกเพิ่มด้วยชื่อเดียวกันดูเหมือนว่าคอลัมน์นั้นจะใช้คอลัมน์เก่าและลบสำหรับเพรดิเคต นี่คือ 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 |
+-------+---------+------+-------+----------+

... ในขณะที่อันนี้เข้าถึง columnstore และส่งกลับอย่างถูกต้อง 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 ใหม่จะแก้ไขข้อมูลที่มีอยู่เท่านั้น หากมีการเพิ่มระเบียนใหม่ปัญหาจะเกิดขึ้นอีกครั้งในบันทึกเหล่านี้ ดังนั้นในปัจจุบันการแก้ไขที่ทราบเพียงอย่างเดียวสำหรับตารางคือการสร้างใหม่ทั้งหมด


1
ไม่ใช่แค่ปัญหาที่ใช้กับตัวเก่าสำหรับภาคแสดงเท่านั้น อีกสิ่งที่แปลกคือมันแบ่งภาคที่เหลือในคอลัมน์ที่แตกต่างกันอย่างสมบูรณ์and id2 = @id2ควรรับประกันศูนย์แถวต่อไปเช่นเดียวกับที่@id2เป็นnullแต่คุณยังคงได้รับ 2
มาร์ตินสมิ ธ

RE: การแก้ไข 2 ของคุณREORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);ทำงานได้หรือไม่ ที่จะล้าง deltastore - ปัญหายังคงเกิดขึ้นสำหรับแถวใหม่ที่เพิ่มเข้ามาหลังจากนั้นหรือไม่?
Martin Smith

ไม่ดูเหมือนว่าจะเป็นผลลัพธ์เดียวกันแน่นอนเศร้า?
Uberzen1

-4

ขึ้นอยู่กับแผนปรากฏว่าดัชนี Columnstore ถูกสร้างขึ้นด้วย SET ANSI_NULLS OFF ตารางและดรรชนียังคงการตั้งค่าเหมือนเดิมเมื่อสร้างดัชนี คุณสามารถตรวจสอบได้ด้วยการสร้างดัชนี Columnstore ที่ซ้ำกันในขณะที่มั่นใจว่า ANSI_NULLS เปิดอยู่จากนั้นปล่อยต้นฉบับหรือปิดใช้งาน

แต่ถ้าคุณไม่พบข้อผิดพลาดของ SQL Server นี่เป็นวิธีเดียวที่ผลลัพธ์อาจเกิดขึ้นได้


2
คุณแน่ใจหรือไม่ว่า 1) ดัชนีที่ไม่ผ่านการกรองสามารถรักษาการตั้งค่า ANSI_NULLS แยกจากตารางฐานและ 2) การตั้งค่าเซสชัน ANSI_NULLS อาจทำให้เกิดความคลาดเคลื่อนเมื่อสร้างตารางด้วย ANSI_NULLS OFF จริงหรือไม่
Forrest

ฉันคิดอย่างนั้น แต่เมื่อฉันเขียนคำจำกัดความของ CCI มันไม่มีตัวเลือกการตั้งค่าและถ้าฉันสร้างด้วย SET ANSI_NULLS ON ก่อนที่คำจำกัดความดัชนีผลลัพธ์จะเหมือนกันหรือไม่
Uberzen1
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.