วิธีเพิ่มประสิทธิภาพการค้นหา


9

ฉันมีโครงสร้างฐานข้อมูลคล้ายกับสิ่งนี้

CREATE TABLE [dbo].[Dispatch](
    [DispatchId] [int] NOT NULL,
    [ContractId] [int] NOT NULL,
    [DispatchDescription] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Dispatch] PRIMARY KEY CLUSTERED 
(
    [DispatchId] ASC,
    [ContractId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[DispatchLink](
    [ContractLink1] [int] NOT NULL,
    [DispatchLink1] [int] NOT NULL,
    [ContractLink2] [int] NOT NULL,
    [DispatchLink2] [int] NOT NULL
) ON [PRIMARY]

GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (1, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (2, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (3, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (4, 1, N'Test')
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 1, 1, 2)
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 1, 1, 3)
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 3, 1, 2)
GO

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

ดังนั้นคำถามของฉันถ้าฉันเรียกใช้แบบสอบถามนี้

select * from Dispatch d
inner join DispatchLink dl on d.DispatchId = dl.DispatchLink1 and d.ContractId = dl.ContractLink1
or d.DispatchId = dl.DispatchLink2 and d.ContractId = dl.ContractLink2

ฉันไม่สามารถทำให้ดัชนีค้นหาบนตาราง DispatchLink ได้ มันจะทำการสแกนดัชนีแบบเต็มเสมอ นั่นเป็นเรื่องปกติที่มีระเบียนไม่กี่รายการ แต่เมื่อคุณมี 50,000 รายการในตารางนั้นจะสแกน 50000 ระเบียนในดัชนีตามแผนแบบสอบถาม เป็นเพราะมี 'ands' และ 'ors' ในส่วนคำสั่งการเข้าร่วม แต่ฉันไม่สามารถเข้าใจได้ว่าทำไม SQL ไม่สามารถทำดัชนีสองสามรายการแทนหนึ่งสำหรับด้านซ้ายของ 'หรือ' และอีกอันสำหรับด้านขวาของ 'หรือ'

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

UPDATE: เช่นนี้เป็นประเภทของดัชนีที่ฉันเพิ่ม

CREATE NONCLUSTERED INDEX IDX1 ON DispatchLink (ContractLink1, DispatchLink1)
CREATE NONCLUSTERED INDEX IDX2 ON DispatchLink (ContractLink2, DispatchLink2)
CREATE NONCLUSTERED INDEX IDX3 ON DispatchLink (ContractLink1, DispatchLink1, ContractLink2, DispatchLink2)

ดังนั้นจึงใช้ดัชนี แต่ทำการสแกนดัชนีทั่วทั้งดัชนีดังนั้น 50,000 บันทึกจะสแกน 50000 บันทึกในดัชนี


คุณมีดัชนีบนDispatchLinkโต๊ะหรือไม่?
ypercubeᵀᴹ

ฉันได้เพิ่มดัชนีที่ฉันได้ลองไปแล้ว
เตอร์

ในแบบสอบถามของคุณ: "select * จาก Dispatch d Inner join DispatchLink dl บน d.DispatchId = dl.DispatchLink1 และ d.ContractId = dl.ContractLink1 หรือ d.DispatchId = dl.DispatchLink2 และ d.ContractIdd = dl.ContractLink2" เงื่อนไข "OR" และแทนที่โดย UNION ของ 2 SELECT คำสั่งแต่ละคำสั่งที่ไม่มี "OR" ให้ใช้คอลัมน์คีย์เดียวใน SELECT ทั้งสองแทน "*" เพียงเพื่อให้การทดสอบนั้นบริสุทธิ์ที่สุด
NoChance

ขอบคุณ SQL Kiwi นี่คือสิ่งที่ฉันเคยลองมาก่อนหน้านี้ แต่มันก็ไม่ได้ผล
เตอร์

1
คุณช่วยให้การจำลองแบบมีการสืบค้นที่ง่ายขึ้นได้อย่างไร: เลือก * จาก Dispatch d Inner join DispatchLink dl บน d.DispatchId = dl.DispatchLink1 และ d.ContractId = dl.ContractLink1 ถ้าใช่เราสามารถทำซ้ำข้อมูลใน DispatchLink ...
AK

คำตอบ:


12

เครื่องมือเพิ่มประสิทธิภาพสามารถพิจารณาทางเลือกแผนจำนวนมาก (รวมถึงตัวเลือกที่มีการค้นหาหลายรายการ) แต่สำหรับการแยก (ภาคORแสดง) จะไม่พิจารณาแผนที่เกี่ยวข้องกับการแยกดัชนีตามค่าเริ่มต้น รับดัชนี:

CREATE CLUSTERED INDEX cx 
ON dbo.DispatchLink (DispatchLink1, ContractLink1);

CREATE NONCLUSTERED INDEX nc1 
ON dbo.DispatchLink (DispatchLink2, ContractLink2);

เราสามารถบังคับให้ค้นหาดัชนี (สมมติว่า SQL Server 2008 หรือใหม่กว่า):

SELECT * 
FROM dbo.Dispatch AS d
INNER JOIN dbo.DispatchLink AS dl WITH (FORCESEEK) ON 
    (d.DispatchId = dl.DispatchLink1 AND d.ContractId = dl.ContractLink1)
    OR (d.DispatchId = dl.DispatchLink2 AND d.ContractId = dl.ContractLink2);

วางแผน FORCESEEK

ใช้ข้อมูลตัวอย่างของคุณค้นหาค่าใช้จ่ายแผนที่0.0332551หน่วยเมื่อเทียบกับ0.0068057สำหรับแผนการสแกน:

สแกนแผน

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

SELECT * 
FROM dbo.Dispatch AS d
CROSS APPLY
(
    SELECT TOP (1) * FROM
    (
        SELECT * FROM dbo.DispatchLink AS dl
        WHERE dl.DispatchLink1 = d.DispatchId
        AND dl.ContractLink1 = d.ContractId
        UNION ALL
        SELECT * FROM dbo.DispatchLink AS dl
        WHERE dl.DispatchLink2 = d.DispatchId
        AND dl.ContractLink2 = d.ContractId
    ) SQ1
) AS F1;

แผนการดำเนินการนี้ไม่ได้แสวงหาดัชนีที่สองหากพบการแข่งขันในรายการแรก:

สมัครผังยอดนิยม

สิ่งนี้อาจทำงานได้ดีกว่าFORCESEEKแผนเริ่มต้นเล็กน้อย

โดยไม่เพิ่มดัชนีใหม่ใด ๆ เราสามารถบังคับให้ค้นหาเข้าไปในตาราง Dispatch ได้:

SELECT * 
FROM dbo.DispatchLink AS dl
JOIN dbo.Dispatch AS d WITH (FORCESEEK) ON
    (d.DispatchId = dl.DispatchLink1 AND d.ContractId = dl.ContractLink1)
    OR (d.DispatchId = dl.DispatchLink2 AND d.ContractId = dl.ContractLink2);

แสวงหา 2

นี่อาจจะดีกว่าหรือแย่กว่าตัวอย่างแรกขึ้นอยู่กับสิ่งต่าง ๆ เช่นจำนวนแถวในแต่ละตาราง การAPPLY + TOPปรับปรุงยังคงเป็นไปได้:

SELECT * 
FROM dbo.DispatchLink AS dl
CROSS APPLY
(
    SELECT TOP (1) * FROM
    (
        SELECT * FROM dbo.Dispatch AS d
        WHERE dl.DispatchLink1 = d.DispatchId
        AND dl.ContractLink1 = d.ContractId
        UNION ALL
        SELECT * FROM dbo.Dispatch AS d
        WHERE dl.DispatchLink2 = d.DispatchId
        AND dl.ContractLink2 = d.ContractId
    ) SQ1
) AS F1;

นั่นเป็นคำตอบที่มีประโยชน์มาก ฉันได้ถามคำถามอื่นdba.stackexchange.com/questions/23773/analysing-a-query-planซึ่งแสดงแผนการสืบค้นจริงในข้อมูลจริง (ไม่ใช่ข้อมูลทดสอบของฉัน) ฉันไม่มีความรู้ที่จะเข้าใจสิ่งที่เป็นปัญหาคอขวดในแผนแบบสอบถาม บางทีคุณสามารถดูได้หรือไม่
เตอร์

มันน่าสนใจจริง ๆ เพราะการเพิ่ม 'FORCESEEK' ทำให้การสืบค้นของฉันทำงานใน 9 วินาทีแทนที่จะใช้เวลามากกว่า 10 นาที สถิติการอัพเดทไม่มีความแตกต่าง ทำไมตัววิเคราะห์แบบสอบถามจึงทำให้เกิดความผิดพลาดขึ้นอีก
เตอร์

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

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