คำเตือน : บางสิ่งในคำตอบนี้อาจทำให้ DBA สะดุ้ง ฉันกำลังเข้าใกล้จากจุดยืนของประสิทธิภาพที่แท้จริง - วิธีรับดัชนีค้นหาเมื่อคุณรับดัชนีสแกน
เมื่อพ้นทางแล้วล่ะก็
ข้อความค้นหาของคุณคือสิ่งที่รู้จักกันในชื่อ "แบบสอบถามอ่างล้างจานในครัว" - ข้อความค้นหาเดียวหมายถึงการตอบสนองเงื่อนไขการค้นหาที่หลากหลาย หากผู้ใช้ตั้ง@status
ค่าคุณต้องการกรองสถานะนั้น หาก@status
เป็นNULL
เช่นนั้นให้ส่งคืนสถานะทั้งหมดและอื่น ๆ
สิ่งนี้จะนำเสนอปัญหาเกี่ยวกับการจัดทำดัชนี แต่ไม่เกี่ยวข้องกับการระบุเป้าหมายเนื่องจากเงื่อนไขการค้นหาทั้งหมดของคุณเป็นเกณฑ์ "เท่ากับ"
นี่คือ sargable:
WHERE [status]=@status
สิ่งนี้ไม่สามารถทำได้เนื่องจาก SQL Server ต้องการประเมินISNULL([status], 0)
สำหรับทุกแถวแทนที่จะค้นหาค่าเดียวในดัชนี:
WHERE ISNULL([status], 0)=@status
ฉันได้สร้างอ่างครัวที่มีปัญหาใหม่ในรูปแบบที่เรียบง่าย:
CREATE TABLE #work (
A int NOT NULL,
B int NOT NULL
);
CREATE UNIQUE INDEX #work_ix1 ON #work (A, B);
INSERT INTO #work (A, B)
VALUES (1, 1), (2, 1),
(3, 1), (4, 1),
(5, 2), (6, 2),
(7, 2), (8, 3),
(9, 3), (10, 3);
หากคุณลองทำสิ่งต่อไปนี้คุณจะได้รับการสแกนดัชนีแม้ว่า A เป็นคอลัมน์แรกของดัชนี:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE (@a IS NULL OR @a=A) AND
(@b IS NULL OR @b=B);
อย่างไรก็ตามสิ่งนี้สร้างดัชนีการค้นหา:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL;
ตราบใดที่คุณใช้พารามิเตอร์จำนวนที่จัดการได้ (สองตัวในกรณีของคุณ) คุณอาจจะแค่UNION
ค้นหาข้อความจำนวนมากโดยทั่วไปแล้วการเรียงสับเปลี่ยนทั้งหมดของเกณฑ์การค้นหา หากคุณมีเกณฑ์สามข้อนี้จะดูยุ่งเหยิงโดยมีสี่ข้อที่ไม่สามารถจัดการได้อย่างสมบูรณ์ คุณได้รับการเตือน
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL
UNION ALL
SELECT *
FROM #work
WHERE @a=A AND
@b=B
UNION ALL
SELECT *
FROM #work
WHERE @a IS NULL AND
@b=B
UNION ALL
SELECT *
FROM #work
WHERE @a IS NULL AND
@b IS NULL;
สำหรับหนึ่งในสามของผู้ที่สี่ที่จะใช้ดัชนีแสวงหาคุณจะต้องมีดัชนีที่สองใน(B, A)
แต่ ต่อไปนี้เป็นวิธีที่คิวรีของคุณอาจดูด้วยการเปลี่ยนแปลงเหล่านี้ (รวมถึงการสร้างการสืบค้นใหม่เพื่อให้สามารถอ่านได้มากขึ้น)
DECLARE @Status int = NULL,
@IsUserGotAnActiveDirectoryUser bit = NULL;
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser IS NULL
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser=1 AND ActiveDirectoryUser<>''
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser=0 AND (ActiveDirectoryUser IS NULL OR ActiveDirectoryUser='')
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser IS NULL
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser=1 AND ActiveDirectoryUser<>''
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser=0 AND (ActiveDirectoryUser IS NULL OR ActiveDirectoryUser='');
... รวมทั้งคุณจะต้องมีดัชนีเพิ่มเติมEmployee
โดยที่ดัชนีคอลัมน์ทั้งสองกลับด้าน
เพื่อความสมบูรณ์ควรจะกล่าวถึงที่x=@x
หมายโดยปริยายว่าx
ไม่สามารถเป็นNULL
เพราะจะไม่เท่ากับNULL
NULL
ทำให้แบบสอบถามง่ายขึ้นเล็กน้อย
และใช่คำตอบ SQL แบบไดนามิกของ Aaron Bertrand เป็นตัวเลือกที่ดีกว่าในกรณีส่วนใหญ่ (เช่นเมื่อใดก็ตามที่คุณสามารถอยู่กับ recompiles ได้
@Status
หรือไม่