โดยทั่วไปฉันแนะนำไม่ให้ใช้คำแนะนำการเข้าร่วมด้วยเหตุผลมาตรฐานทั้งหมด อย่างไรก็ตามเมื่อเร็ว ๆ นี้ฉันได้พบรูปแบบที่ฉันมักจะพบว่าวงบังคับที่ถูกบังคับให้ทำงานได้ดีขึ้น ในความเป็นจริงฉันเริ่มใช้และแนะนำมันมากจนฉันต้องการได้รับความเห็นที่สองเพื่อให้แน่ใจว่าฉันจะไม่พลาดบางสิ่งบางอย่าง นี่คือสถานการณ์จำลอง (รหัสที่เจาะจงมากในการสร้างตัวอย่างคือตอนท้าย):
--Case 1: NO HINT
SELECT S.*
INTO #Results
FROM #Driver AS D
JOIN SampleTable AS S ON S.ID = D.ID
--Case 2: LOOP JOIN HINT
SELECT S.*
INTO #Results
FROM #Driver AS D
INNER LOOP JOIN SampleTable AS S ON S.ID = D.ID
SampleTable มี 1 ล้านแถวและ PK คือ ID
ตารางชั่วคราว #Driver มีเพียงหนึ่งคอลัมน์ ID ไม่มีดัชนีและแถว 50K
สิ่งที่ฉันค้นหาอย่างสม่ำเสมอมีดังต่อไปนี้:
กรณีที่ 1: ไม่มีคำแนะนำการ
สแกนดัชนี
แฮชของSampleTable เข้าร่วม
ระยะเวลาที่สูงขึ้น (เฉลี่ย 333 มิลลิวินาที)
CPU ที่สูงขึ้น (เฉลี่ย 331 มิลลิวินาที) การ
อ่านตรรกะที่ต่ำกว่า (4714)
กรณีที่ 2: LOOP เข้าร่วม
ดัชนีคำแนะนำค้นหา SampleTable
Loop เข้าร่วมช่วง
เวลาที่ต่ำกว่า (เฉลี่ย 204ms, 39% น้อยกว่า)
CPU ที่ลดลง (เฉลี่ย 206, 38% น้อยกว่า)
อ่านตรรกะที่สูงขึ้นมาก (160015, 34X เพิ่มเติม)
ตอนแรกการอ่านกรณีที่สองที่สูงขึ้นมากทำให้ฉันกลัวเล็กน้อยเพราะการอ่านที่ลดลงมักจะถือว่าเป็นการวัดประสิทธิภาพที่เหมาะสม แต่ยิ่งฉันคิดเกี่ยวกับสิ่งที่เกิดขึ้นจริงมันไม่ได้เกี่ยวข้องกับฉัน นี่คือความคิดของฉัน:
SampleTable มีอยู่ในหน้า 4714 ใช้เวลาประมาณ 36MB กรณีที่ 1 สแกนพวกเขาทั้งหมดซึ่งเป็นสาเหตุที่เราได้รับ 4714 อ่าน ยิ่งไปกว่านั้นจะต้องทำการแฮ็ค 1 ล้านแฮชซึ่งเป็น CPU ที่เข้มข้น ทั้งหมดนี้เป็นเพียงการคร่ำเครียดซึ่งดูเหมือนว่าจะทำให้เวลาในกรณีที่ 1
ตอนนี้ให้พิจารณากรณีที่ 2 มันไม่ได้ทำ hashing ใด ๆ แต่แทนที่จะทำ 50,000 ค้นหาแยกซึ่งเป็นสิ่งที่ผลักดันการอ่าน แต่ราคาอ่านแพงแค่ไหน? บางคนอาจบอกว่าถ้าอ่านแล้วมันอาจมีราคาแพง แต่โปรดจำไว้ว่า 1) เฉพาะการอ่านครั้งแรกของหน้าเว็บที่กำหนดอาจเป็นทางกายภาพและ 2) ดังนั้นแม้ว่ากรณีที่ 1 จะมีปัญหาเดียวกันหรือแย่กว่านั้นเนื่องจากรับประกันว่าจะตีทุกหน้า
ดังนั้นการบัญชีสำหรับข้อเท็จจริงที่ว่าทั้งสองกรณีต้องเข้าถึงแต่ละหน้าอย่างน้อยหนึ่งครั้งดูเหมือนคำถามที่เร็วกว่า 1 ล้านแฮชหรือประมาณ 155000 อ่านกับหน่วยความจำ? การทดสอบของฉันดูเหมือนจะพูดหลัง แต่ SQL Server เลือกอย่างสม่ำเสมอ
คำถาม
ดังนั้นกลับไปที่คำถามของฉัน: ฉันควรบังคับให้คำแนะนำ LOOP JOIN นี้ต่อไปเมื่อการทดสอบแสดงผลลัพธ์เหล่านี้หรือฉันขาดการวิเคราะห์ ฉันลังเลที่จะเปรียบเทียบกับเครื่องมือเพิ่มประสิทธิภาพของ SQL Server แต่รู้สึกว่ามันเปลี่ยนเป็นการใช้แฮชเข้าร่วมเร็วกว่าที่ควรในกรณีเช่นนี้
อัปเดต 2014-04-28
ฉันทำการทดสอบเพิ่มเติมและพบว่าผลลัพธ์ที่ฉันได้รับเหนือกว่า (บน VM w / 2 CPUs) ฉันไม่สามารถทำซ้ำในสภาพแวดล้อมอื่น ๆ (ฉันลองบนเครื่องทางกายภาพ 2 เครื่องที่มี 8 และ 12 CPU) เครื่องมือเพิ่มประสิทธิภาพทำได้ดีขึ้นมากในกรณีหลังจนถึงจุดที่ไม่มีปัญหาเด่นชัดดังกล่าว ฉันเดาว่าบทเรียนที่เรียนรู้ซึ่งดูเหมือนจะชัดเจนในการหวนกลับคือสภาพแวดล้อมสามารถส่งผลกระทบอย่างมีนัยสำคัญต่อวิธีที่เครื่องมือเพิ่มประสิทธิภาพทำงานได้ดี
แผนการดำเนินการ
กรณีแผนปฏิบัติการ 1
กรณีแผนปฏิบัติการ 2

รหัสเพื่อสร้างกรณีตัวอย่าง
------------------------------------------------------------
-- 1. Create SampleTable with 1,000,000 rows
------------------------------------------------------------
CREATE TABLE SampleTable
(
ID INT NOT NULL PRIMARY KEY CLUSTERED
, Number1 INT NOT NULL
, Number2 INT NOT NULL
, Number3 INT NOT NULL
, Number4 INT NOT NULL
, Number5 INT NOT NULL
)
--Add 1 million rows
;WITH
Cte0 AS (SELECT 1 AS C UNION ALL SELECT 1), --2 rows
Cte1 AS (SELECT 1 AS C FROM Cte0 AS A, Cte0 AS B),--4 rows
Cte2 AS (SELECT 1 AS C FROM Cte1 AS A ,Cte1 AS B),--16 rows
Cte3 AS (SELECT 1 AS C FROM Cte2 AS A ,Cte2 AS B),--256 rows
Cte4 AS (SELECT 1 AS C FROM Cte3 AS A ,Cte3 AS B),--65536 rows
Cte5 AS (SELECT 1 AS C FROM Cte4 AS A ,Cte2 AS B),--1048576 rows
FinalCte AS (SELECT ROW_NUMBER() OVER (ORDER BY C) AS Number FROM Cte5)
INSERT INTO SampleTable
SELECT Number, Number, Number, Number, Number, Number
FROM FinalCte
WHERE Number <= 1000000
------------------------------------------------------------
-- Create 2 SPs that join from #Driver to SampleTable.
------------------------------------------------------------
GO
IF OBJECT_ID('JoinTest_NoHint') IS NOT NULL DROP PROCEDURE JoinTest_NoHint
GO
CREATE PROC JoinTest_NoHint
AS
SELECT S.*
INTO #Results
FROM #Driver AS D
JOIN SampleTable AS S ON S.ID = D.ID
GO
IF OBJECT_ID('JoinTest_LoopHint') IS NOT NULL DROP PROCEDURE JoinTest_LoopHint
GO
CREATE PROC JoinTest_LoopHint
AS
SELECT S.*
INTO #Results
FROM #Driver AS D
INNER LOOP JOIN SampleTable AS S ON S.ID = D.ID
GO
------------------------------------------------------------
-- Create driver table with 50K rows
------------------------------------------------------------
GO
IF OBJECT_ID('tempdb..#Driver') IS NOT NULL DROP TABLE #Driver
SELECT ID
INTO #Driver
FROM SampleTable
WHERE ID % 20 = 0
------------------------------------------------------------
-- Run each test and run Profiler
------------------------------------------------------------
GO
/*Reg*/ EXEC JoinTest_NoHint
GO
/*Loop*/ EXEC JoinTest_LoopHint
------------------------------------------------------------
-- Results
------------------------------------------------------------
/*
Duration CPU Reads TextData
315 313 4714 /*Reg*/ EXEC JoinTest_NoHint
309 296 4713 /*Reg*/ EXEC JoinTest_NoHint
327 329 4713 /*Reg*/ EXEC JoinTest_NoHint
398 406 4715 /*Reg*/ EXEC JoinTest_NoHint
316 312 4714 /*Reg*/ EXEC JoinTest_NoHint
217 219 160017 /*Loop*/ EXEC JoinTest_LoopHint
211 219 160014 /*Loop*/ EXEC JoinTest_LoopHint
217 219 160013 /*Loop*/ EXEC JoinTest_LoopHint
190 188 160013 /*Loop*/ EXEC JoinTest_LoopHint
187 187 160015 /*Loop*/ EXEC JoinTest_LoopHint
*/