ไม่สามารถสร้างดัชนีที่กรองแล้วในคอลัมน์จากการคำนวณ


18

ในคำถามก่อนหน้านี้ของฉันเป็นความคิดที่ดีหรือไม่ที่จะปิดใช้งานการเลื่อนระดับล็อคในขณะที่เพิ่มคอลัมน์จากการคำนวณใหม่ลงในตาราง ฉันกำลังสร้างคอลัมน์จากการคำนวณ:

ALTER TABLE dbo.tblBGiftVoucherItem
ADD isUsGift AS CAST
(
    ISNULL(
        CASE WHEN sintMarketID = 2 
            AND strType = 'CARD'
            AND strTier1 LIKE 'GG%' 
        THEN 1 
        ELSE 0 
        END
    , 0) 
    AS BIT
) PERSISTED;

คอลัมน์จากการคำนวณคือPERSISTEDและตามcomputed_column_definition (Transact-SQL) :

ยืนกราน

ระบุว่า Database Engine จะจัดเก็บค่าที่คำนวณในร่างกายและอัพเดตค่าเมื่อคอลัมน์อื่น ๆ ที่คอลัมน์ที่คำนวณขึ้นอยู่นั้นถูกอัพเดต การทำเครื่องหมายคอลัมน์ที่คำนวณเป็น PERSISTED อนุญาตให้สร้างดัชนีบนคอลัมน์ที่คำนวณได้ซึ่งกำหนดไว้ แต่ไม่แม่นยำ สำหรับข้อมูลเพิ่มเติมดูดัชนีในคอลัมน์ที่คำนวณ คอลัมน์ที่คำนวณใด ๆ ที่ใช้เป็นคอลัมน์การแบ่งพาร์ติชันของตารางที่แบ่งพาร์ติชันจะต้องทำเครื่องหมาย PERSISTED อย่างชัดเจน computed_column_expression ต้องกำหนดขึ้นเมื่อ PERSISTED ถูกระบุ

แต่เมื่อฉันพยายามสร้างดัชนีในคอลัมน์ของฉันฉันได้รับข้อผิดพลาดต่อไปนี้:

CREATE INDEX FIX_tblBGiftVoucherItem_incl
ON dbo.tblBGiftVoucherItem (strItemNo) 
INCLUDE (strTier3)
WHERE isUsGift = 1;

ดัชนีที่กรองแล้ว 'FIX_tblBGiftVoucherItem_incl' ไม่สามารถสร้างขึ้นในตาราง 'dbo.tblBGiftVoucherItem' เนื่องจากคอลัมน์ 'isUsGift' ในนิพจน์ตัวกรองเป็นคอลัมน์ที่คำนวณ เขียนซ้ำการแสดงออกของตัวกรองเพื่อที่จะไม่รวมคอลัมน์นี้

ฉันจะสร้างดัชนีที่กรองแล้วในคอลัมน์ที่คำนวณได้อย่างไร

หรือ

มีทางเลือกอื่นหรือไม่?


3
คุณสามารถสร้างดัชนีที่ถูกกรองWHERE (sintMarketID = 2 AND strType = 'CARD' AND strTier1 LIKE 'GG%')ได้
ypercubeᵀᴹ

คำตอบ:


21

น่าเสียดายที่ SQL Server 2014 ไม่มีความสามารถในการสร้างFiltered Indexตัวกรองที่อยู่ในคอลัมน์ที่คำนวณได้ (ไม่ว่าจะคงอยู่หรือไม่ก็ตาม)

มีการเปิดรายการเชื่อมต่อตั้งแต่ปี 2009 ดังนั้นโปรดไปข้างหน้าและลงคะแนนให้ บางที Microsoft อาจแก้ไขปัญหานี้ในหนึ่งวัน

แอรอนเบอร์ทรานด์มีบทความที่ครอบคลุมจำนวนของปัญหาอื่น ๆ ที่มีดัชนีกรอง


21

แม้ว่าคุณจะไม่สามารถสร้างดัชนีที่ถูกกรองในคอลัมน์ที่เก็บไว้ได้ แต่ก็มีวิธีแก้ไขง่ายๆที่คุณสามารถใช้งานได้

ในการทดสอบฉันได้สร้างตารางอย่างง่ายพร้อมIDENTITYคอลัมน์และคอลัมน์ที่คำนวณแล้วซึ่งยังคงอยู่ตามคอลัมน์ข้อมูลประจำตัว:

USE tempdb;

CREATE TABLE dbo.PersistedViewTest
(
    PersistedViewTest_ID INT NOT NULL
        CONSTRAINT PK_PersistedViewTest
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , SomeData VARCHAR(2000) NOT NULL
    , TestComputedColumn AS (PersistedViewTest_ID - 1) PERSISTED
);
GO

จากนั้นฉันสร้างมุมมองที่ถูกผูกไว้กับสกีมาตามตารางที่มีตัวกรองในคอลัมน์ที่คำนวณได้:

CREATE VIEW dbo.PersistedViewTest_View
WITH SCHEMABINDING
AS
SELECT PersistedViewTest_ID
    , SomeData 
    , TestComputedColumn
FROM dbo.PersistedViewTest
WHERE TestComputedColumn < CONVERT(INT, 27);

ถัดไปฉันสร้างดัชนีคลัสเตอร์ในมุมมอง schema-bound ซึ่งมีผลในการเก็บค่าที่เก็บไว้ในมุมมองรวมถึงค่าของคอลัมน์ที่คำนวณได้:

CREATE UNIQUE CLUSTERED INDEX IX_PersistedViewTest
ON dbo.PersistedViewTest_View(PersistedViewTest_ID);
GO

แทรกข้อมูลทดสอบลงในตาราง:

INSERT INTO dbo.PersistedViewTest (SomeData)
SELECT o.name + o1.name + o2.name
FROM sys.objects o
    CROSS JOIN sys.objects o1
    CROSS JOIN sys.objects o2;

สร้างรายการสถิติและดัชนีในมุมมอง:

CREATE STATISTICS ST_PersistedViewTest_View
ON dbo.PersistedViewTest_View(TestComputedColumn)
WITH FULLSCAN;

CREATE INDEX IX_PersistedViewTest_View_TestComputedColumn
ON dbo.PersistedViewTest_View(TestComputedColumn);

การดำเนินการSELECTคำสั่งเทียบกับตารางที่มีคอลัมน์ที่เก็บไว้อาจจะใช้มุมมองที่เก็บไว้โดยอัตโนมัติในขณะนี้หากเครื่องมือเพิ่มประสิทธิภาพการสืบค้นพิจารณาว่าเหมาะสมแล้ว:

SELECT pv.PersistedViewTest_ID
    , pv.TestComputedColumn
FROM dbo.PersistedViewTest pv
WHERE pv.TestComputedColumn = CONVERT(INT, 26)

แผนการดำเนินการตามจริงสำหรับเคียวรีด้านบนแสดงเครื่องมือเพิ่มประสิทธิภาพคิวรีที่เลือกใช้มุมมองที่เก็บไว้เพื่อส่งคืนผลลัพธ์:

ป้อนคำอธิบายรูปภาพที่นี่

คุณอาจสังเกตเห็นการแปลงชัดเจนในWHEREข้อข้างต้น ชัดเจนCONVERT(INT, 26)ช่วยให้เครื่องมือเพิ่มประสิทธิภาพการสืบค้นใช้วัตถุสถิติอย่างถูกต้องเพื่อประเมินจำนวนแถวที่จะส่งคืนโดยแบบสอบถาม ถ้าเราเขียนแบบสอบถามกับWHERE pv.TestComputedColumn = 26เพิ่มประสิทธิภาพการค้นหาอาจจะไม่ถูกต้องประมาณการจำนวนแถวตั้งแต่วันที่ 26 ถือว่าเป็นจริงTINY INT; นี่อาจทำให้ SQL Server ไม่ใช้มุมมองที่มีอยู่ การแปลงโดยนัยอาจเจ็บปวดมากและจ่ายเพื่อใช้ชนิดข้อมูลที่ถูกต้องสำหรับการเปรียบเทียบและการรวมอย่างสม่ำเสมอ

แน่นอน "gotchas" มาตรฐานทั้งหมดที่เกิดจากการใช้ schema binding จะใช้กับสถานการณ์ข้างต้น สิ่งนี้อาจป้องกันไม่ให้ใช้วิธีแก้ปัญหานี้ในทุกสถานการณ์ ตัวอย่างเช่นจะไม่สามารถแก้ไขตารางพื้นฐานได้อีกต่อไปหากไม่ลบการเชื่อมสคีมาออกจากมุมมองก่อน ในการดำเนินการดังกล่าวคุณจะต้องลบดัชนีคลัสเตอร์ออกจากมุมมอง

หากคุณไม่มี SQL Server Enterprise Edition ตัวเพิ่มประสิทธิภาพการสืบค้นจะไม่ใช้มุมมองที่เก็บไว้โดยอัตโนมัติสำหรับแบบสอบถามที่ไม่ได้อ้างอิงมุมมองโดยตรงโดยใช้WITH (NOEXPAND)คำใบ้ หากต้องการทราบถึงประโยชน์ของการใช้มุมมองที่ยังคงมีอยู่ในเวอร์ชันที่ไม่ใช่ขององค์กรคุณจะต้องเขียนข้อความค้นหาข้างต้นอีกครั้งเพื่อ:

SELECT pv.PersistedViewTest_ID
    , pv.TestComputedColumn
FROM dbo.PersistedViewTest_View pv WITH (NOEXPAND)
WHERE pv.TestComputedColumn = CONVERT(INT, 26)

ขอบคุณIan Ringrose ที่ชี้ให้เห็นข้อ จำกัด ของ Enterprise Edition ด้านบนและPaul Whiteสำหรับ(NOEXPAND)คำใบ้

คำตอบของ Paulมีรายละเอียดที่น่าสนใจเกี่ยวกับเครื่องมือเพิ่มประสิทธิภาพข้อความค้นหาที่เกี่ยวข้องกับมุมมองที่ยังคงอยู่


การทำงานรอบ ๆ แสดงให้เห็นว่าทั้งดัชนีคลัสเตอร์และดัชนีแบบไม่รวมกลุ่มถูกสร้างขึ้นในมุมมอง ต้องใช้ดัชนีที่ไม่ใช่คลัสเตอร์ในดัชนีที่จัดทำคลัสเตอร์ด้วยเหตุผลบางประการหรือไม่? หรือดัชนีแบบ nonclustered มีประสิทธิภาพมากกว่าหรือไม่? หากมีการใช้ดัชนีคลัสเตอร์ในแบบสอบถามสถิติจะแสดงอะไร
บ็อบไบรอัน

คำถามที่น่าสนใจ @BobBryan - ดัชนีคลัสเตอร์จะต้องเปิดใช้งานมุมมองให้คงอยู่แม้ว่าจริง ๆ แล้วมันไม่จำเป็นต้องเป็นดัชนีที่ไม่ซ้ำกัน ฉันสามารถสร้างดัชนีคลัสเตอร์ของมุมมองในคอลัมน์อื่น ๆ ได้เช่นTestComputedColumnแทน อย่างไรก็ตามเนื่องจากดัชนีคลัสเตอร์มีข้อมูลทั้งหมดสำหรับตาราง / มุมมองฉันจึงตัดสินใจว่าน่าจะดีกว่าถ้าใช้ตัวเลขที่เพิ่มขึ้นแบบจำเจเป็นคีย์การทำคลัสเตอร์ หมายเหตุฉันไม่ได้ทดสอบการคาดคะเนนั้นและในความเป็นจริงอาจไม่ถูกต้องสำหรับการจำลองบางอย่าง
Max Vernon

หมายเหตุดัชนีที่ไม่ใช่คลัสเตอร์ไม่ใช่ดัชนีครอบคลุมและเช่นแบบสอบถามใด ๆ ที่กรองบนเชื่อมต่อหรือส่งคืนคอลัมน์จากมุมมองหรือตารางต้นแบบจะต้องดำเนินการค้นหาคีย์กับตารางฐานหรือ มุมมอง. เป็นไปได้ว่าสำหรับสถานการณ์ในโลกแห่งความเป็นจริงขอบเขตที่ จำกัด ของคำตอบของฉันอาจถูกอธิบายด้วยประสิทธิภาพที่ดียิ่งขึ้น
Max Vernon

4

จากCreate Indexและwhereข้อของมันเป็นไปไม่ได้:

WHERE

สร้างดัชนีที่กรองโดยระบุแถวที่จะรวมในดัชนี ดัชนีที่กรองจะต้องเป็นดัชนีที่ไม่เป็นคลัสเตอร์ในตาราง สร้างสถิติที่กรองสำหรับแถวข้อมูลในดัชนีที่กรอง

เพรดิเคตตัวกรองใช้ตรรกะการเปรียบเทียบแบบง่าย ๆ และไม่สามารถอ้างอิงคอลัมน์ที่คำนวณได้, คอลัมน์ UDT, คอลัมน์ชนิดข้อมูลเชิงพื้นที่หรือคอลัมน์ประเภทข้อมูลลำดับชั้น ID ไม่อนุญาตการเปรียบเทียบที่ใช้ตัวอักษร NULL กับตัวดำเนินการเปรียบเทียบ ใช้ IS NULL และ IS NOT NULL โอเปอเรเตอร์แทน

ที่มา: MSDN


3
  • คุณต้องมีคอลัมน์ที่ไม่ได้คำนวณเพื่อใส่ดัชนีที่กรอง
  • คุณต้องคำนวณค่าเพื่อไปที่คอลัมน์นั้น

ก่อนที่เราจะคำนวณคอลัมน์เราใช้ทริกเกอร์ในการคำนวณค่าคอลัมน์ทุกครั้งที่มีการเปลี่ยนแปลงหรือแทรกแถว

(นอกจากนี้ยังสามารถใช้ทริกเกอร์เพื่อแทรก / ลบ PK ของรายการจากตารางที่ 2 ที่ใช้ในการสืบค้น)


3

นี้เป็นความพยายามที่จะปรับปรุงรอบการทำงานสูงสุดเวอร์นอน ในการแก้ปัญหาของเขาเขาแนะนำให้ใช้ 2 ดัชนีในมุมมองและวัตถุสถิติ

ดัชนีที่ 1 คือคลัสเตอร์ซึ่งจำเป็นจริง ๆ แล้วเนื่องจากไม่เหมือนกับดัชนีที่ไม่ใช่คลัสเตอร์บนตารางข้อผิดพลาดจะถูกสร้างขึ้นหากการสร้างดัชนีที่ไม่ได้คลัสเตอร์บนมุมมองนั้นจะพยายามทำโดยไม่ต้องมีดัชนีคลัสเตอร์แรก

ดัชนีที่ 2 เป็นดัชนีที่ไม่เป็นคลัสเตอร์ซึ่งใช้เป็นดัชนีที่อยู่เบื้องหลังแบบสอบถาม ในส่วนความเห็นของคำตอบของเขาฉันถามว่าจะเกิดอะไรขึ้นถ้ามีการใช้ดัชนีแบบคลัสเตอร์แทนดัชนีแบบไม่รวมกลุ่ม

การวิเคราะห์ต่อไปนี้พยายามตอบคำถามนี้

ฉันใช้รหัสเดียวกันของเขายกเว้นว่าฉันไม่ได้สร้างดัชนีแบบ nonclustered ในมุมมอง

ฉันยังไม่ได้สร้างวัตถุสถิติ หากคุณกำลังติดตามและใช้ SQL Server Management Studio (SSMS) เพื่อป้อนรหัสด้านล่างคุณควรทราบว่าคุณอาจเห็นเส้นสีแดงบาง ๆ ซึ่งดูเหมือนว่ามีข้อผิดพลาด สิ่งเหล่านี้ (อาจ) ไม่ใช่ข้อผิดพลาด แต่เกี่ยวข้องกับปัญหาที่เกิดกับ Intellisense

คุณสามารถปิดใช้งาน Intellisense หรือเพียงแค่ละเว้นข้อผิดพลาดและเรียกใช้คำสั่ง ควรดำเนินการให้เสร็จสมบูรณ์โดยไม่มีข้อผิดพลาด

-- Create the test table that uses a computed column.
USE tempdb;
CREATE TABLE dbo.PersistedViewTest
(
    PersistedViewTest_ID INT NOT NULL
    CONSTRAINT PK_PersistedViewTest
    PRIMARY KEY CLUSTERED
    IDENTITY(1,1)
    , SomeData VARCHAR(2000) NOT NULL
    , TestComputedColumn AS (PersistedViewTest_ID - 1) PERSISTED
);
GO

-- Insert some test data into the table.
INSERT INTO dbo.PersistedViewTest (SomeData)
SELECT o.name + o1.name + o2.name
FROM sys.objects o
    CROSS JOIN sys.objects o1
    CROSS JOIN sys.objects o2;
GO

แผนการดำเนินการต่อไปนี้ (ไม่มีมุมมอง / มุมมองดัชนี) ถูกสร้างขึ้นหลังจากเรียกใช้แบบสอบถามต่อไปนี้กับตาราง:

SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
FROM dbo.PersistedViewTest pv
WHERE pv.TestComputedColumn = CONVERT(INT, 26)
GO

ป้อนคำอธิบายรูปภาพที่นี่

สิ่งนี้จะให้พื้นฐานเพื่อเปรียบเทียบกับ โปรดสังเกตว่าหลังจากแบบสอบถามเสร็จสมบูรณ์วัตถุสถิติถูกสร้างขึ้น (_WA_Sys_00000003_1FCDBCEB) อ็อบเจ็กต์สถิติ PK_PersistedViewTest ถูกสร้างขึ้นเมื่อสร้างดัชนีตารางคลัสเตอร์

ถัดไปมุมมองที่กรองและดัชนีคลัสเตอร์ในมุมมองนั้นจะถูกสร้างขึ้น:

-- Create filtered view on the computed column.
CREATE VIEW dbo.PersistedViewTest_View
WITH SCHEMABINDING
AS
SELECT PersistedViewTest_ID, SomeData, TestComputedColumn
FROM dbo.PersistedViewTest
WHERE TestComputedColumn < CONVERT(INT, 27);
GO

-- Create unique clustered index to persist the values, including the computed column.
CREATE UNIQUE CLUSTERED INDEX IX_PersistedViewTest
ON dbo.PersistedViewTest_View(PersistedViewTest_ID);
GO

ตอนนี้ให้ลองเรียกใช้แบบสอบถามอีกครั้ง แต่ครั้งนี้เทียบกับมุมมอง:

SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
FROM dbo.PersistedViewTest_View pv
WHERE pv.TestComputedColumn = CONVERT(INT, 26)
GO

แผนการดำเนินการใหม่คือตอนนี้:

ป้อนคำอธิบายรูปภาพที่นี่

หากต้องเชื่อแผนใหม่หลังจากเพิ่มมุมมองและดัชนีคลัสเตอร์ในมุมมองนั้นสถิติจะปรากฏขึ้นเพื่อระบุว่าเวลาที่ต้องใช้ในการเรียกใช้แบบสอบถามได้เพิ่มขึ้นเป็นสองเท่า นอกจากนี้โปรดสังเกตว่าไม่มีการสร้างวัตถุสถิติใหม่เพื่อสนับสนุนดัชนีใหม่หลังจากเรียกใช้แบบสอบถามซึ่งแตกต่างจากแบบสอบถามในตาราง

แผนแบบสอบถามยังแนะนำว่าการสร้างดัชนีที่ไม่ได้เป็นคลัสเตอร์จะค่อนข้างมีประโยชน์ในการปรับปรุงประสิทธิภาพของแบบสอบถาม ดังนั้นนั่นหมายความว่าจะต้องเพิ่มดัชนีที่ไม่ได้เป็นคลัสเตอร์ในมุมมองก่อนที่จะสามารถปรับปรุงประสิทธิภาพที่ต้องการได้ มีสิ่งสุดท้ายที่ต้องลอง ปรับเปลี่ยนแบบสอบถามเพื่อใช้ตัวเลือก "WITH NOEXPAND":

SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
FROM dbo.PersistedViewTest_View pv WITH (NOEXPAND)
WHERE pv.TestComputedColumn = CONVERT(INT, 26)
GO

ผลลัพธ์นี้ในแผนแบบสอบถามต่อไปนี้:

ป้อนคำอธิบายรูปภาพที่นี่

แผนการดำเนินการนี้มีลักษณะค่อนข้างคล้ายกับแผนการผลิตที่ไม่ได้จัดกลุ่มไว้ในคำตอบของ Max Vernon แต่อันนี้ทำได้ด้วยดัชนีที่น้อยกว่า (ไม่มีการรวมกลุ่ม) และวัตถุสถิติที่น้อยกว่าหนึ่งรายการ

ปรากฎว่าต้องใช้ตัวเลือก NOEXPAND กับ SQL Server รุ่นด่วนและมาตรฐานเพื่อใช้มุมมองที่จัดทำดัชนีอย่างเหมาะสม Paul White มีบทความที่ยอดเยี่ยมซึ่งจะอธิบายเกี่ยวกับประโยชน์ของการใช้ตัวเลือก NOEXPAND นอกจากนี้เขายังแนะนำให้ใช้ตัวเลือกนี้กับรุ่นองค์กรเพื่อให้มั่นใจถึงการรับประกันที่ไม่ซ้ำกันของดัชนีมุมมองที่ใช้โดยเครื่องมือเพิ่มประสิทธิภาพ

การวิเคราะห์ข้างต้นดำเนินการกับรุ่นด่วนของ SQL Sever 2014 ฉันยังลองใช้กับรุ่นนักพัฒนาของ SQL Server 2016 ตัวเลือก NOEXPAND ดูเหมือนจะไม่จำเป็นต้องใช้กับรุ่นพัฒนาเพื่อให้ได้ประสิทธิภาพ แต่ก็ยังแนะนำ .

น้อยกว่า 5 เดือนที่ผ่านมาไมโครซอฟท์ทำรุ่นนักพัฒนาอิสระ ใบอนุญาต จำกัด การใช้เพื่อการพัฒนาเท่านั้นซึ่งหมายความว่าฐานข้อมูลไม่สามารถใช้ในสภาพแวดล้อมการผลิต ดังนั้นหากคุณต้องการทดสอบตารางที่เพิ่มประสิทธิภาพหน่วยความจำการเข้ารหัส R และอื่น ๆ คุณจะไม่มีข้อแก้ตัวที่ไม่มีใบอนุญาตอีกต่อไป ฉันติดตั้งมันบนคอมพิวเตอร์ของฉันไม่กี่วันที่ผ่านมาพร้อมกับ SQL Server 2014 Express โดยไม่มีปัญหา

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