ประสิทธิภาพของ a = 0 และ b = 0 และ… z = 0 vs a + b + c + d = 0


20

นี่เป็นคำถามง่าย ๆ ที่ฉันไม่สามารถหาคำตอบได้

ในแง่ของประสิทธิภาพถ้าฉันมีWHEREประโยคเช่นa=0 and b=0 and ... z=0ฉันจะได้รับประสิทธิภาพใด ๆ หรือไม่ถ้าฉันเปลี่ยนเงื่อนไขนั้นด้วยa+b+...+z=0?

กล่าวอีกนัยหนึ่งมีการเพิ่มประสิทธิภาพใด ๆ โดยการแทนที่ต่อไปนี้

Select * 
From MyTable 
Where A=0 and B=0 and C=0 and D=0...

กับ

Select * 
From MyTable 
Where A+B+C+D=0...

ฉันรู้ว่ามันสามารถขึ้นอยู่กับดัชนีได้ แต่สำหรับจุดประสงค์นี้สมมุติว่าไม่มีดัชนีอยู่ ตัวดำเนินการทางคณิตศาสตร์ (+) ทำงานได้ดีกว่าตัวดำเนินการทางตรรกะ "OR" หรือ "AND" หรือไม่?

ฉันอยู่ภายใต้การแสดงผลว่าการเพิ่มประสิทธิภาพนั้นดีกว่าหลายเงื่อนไขด้วย ANDs หรือ OR

ผลการทดสอบ

บนตาราง 4.2 ล้านแถว

การส่งคืนแถวโดยที่ A = 0 B = 0 และ C = 0 -> แถว 351748

การเพิ่ม (A + B + C = 0) ใช้เวลา 5 วินาทีในขณะที่เงื่อนไขเชิงตรรกะ A = 0 และ B = 0 และ C = 0 ใช้เวลา 11 วินาที

ในทางกลับกัน

การคืนแถวโดยที่ A <> 0 B <> 0 หรือ C <> 0 -> 3829750 แถว 58 วินาที

การกลับแถวที่ F65 + F67 + f64 <> 0 -> 3829750 แถว 57 วินาที

สำหรับ OR ดูเหมือนว่าไม่มีความแตกต่างอย่างมีนัยสำคัญ

ฉันเห็นด้วยกับ gbn:

หาก A คือ -1 และ B คือ 1 A + B = 0 แต่ A = 0 และ B = 0 เป็นเท็จ

และกับ AMtwo:

ABS (A) + ABS (B) + ABS (C) + ABS (D) ... แม้ว่าคุณจะคาดหวังเพียงค่าบวกหากคอลัมน์ยอมรับค่าลบคุณควรคิดว่าคุณอาจพบ

ผลลัพธ์เป็นที่น่าประทับใจอย่างที่ฉันคิดว่าดูเหมือนว่าการเพิ่มจะเร็วกว่าตัวดำเนินการเชิงตรรกะมาก

A = Float, B = Money และ C = Float แบบสอบถามที่ใช้นั้นเป็นไปตามที่แสดง ในกรณีของฉันทั้งหมดเป็นจำนวนบวก ไม่มีดัชนี มันมีเหตุผลในใจของฉันว่าการเติมจะเร็วกว่าเงื่อนไขเชิงตรรกะ!


บูลีนเหล่านี้หรือไม่ มีกี่คอลัมน์ที่คุณพูดถึง 4 (ในตัวอย่าง) หรือ 26 (ในชื่อเรื่อง) มันสร้างความแตกต่าง SQL Server รุ่นใด FLOAT และ MONEY เล่นที่ไหน? เราคิดว่ามีกี่แถว คำถามนี้มีปัจจัยมากมาย
Evan Carroll

@Evan Carroll ไม่ใช่บูลีน แต่เป็นตัวเลขที่ไม่ได้จัดทำดัชนี (int, float, money และอื่น ๆ ) ไม่ว่าจะใช้ SQL เวอร์ชันใด (SQL2012 ขึ้นไป) จำนวนแถวหรือคอลัมน์คำถามก็คือการค้นหาว่าตัวดำเนินการใดที่มีประสิทธิภาพดีกว่า - ตัวดำเนินการทางตรรกะ vs เลขคณิต อย่างที่คุณเห็น Max Vernon สาธิตทฤษฎีด้วยตัวอย่างของเขาอย่างสมบูรณ์แบบ
JohnG

คำตอบ:


46

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

ก่อนอื่นคุณต้องตรวจสอบให้แน่ใจว่าคุณไม่ได้ทดสอบ SQL Server Management Studio (หรือไคลเอ็นต์ใดก็ตามที่คุณใช้) ตัวอย่างเช่นหากคุณเรียกใช้SELECT *จากตารางที่มี 3 ล้านแถวส่วนใหญ่คุณกำลังทดสอบความสามารถของ SSMS ในการดึงแถวจาก SQL Server และแสดงผลบนหน้าจอ คุณดีกว่าที่จะใช้สิ่งSELECT COUNT(1)ที่ขัดแย้งกับความต้องการดึงแถวหลายล้านแถวทั่วทั้งเครือข่ายและแสดงบนหน้าจอ

ประการที่สองคุณต้องระวังแคชข้อมูลของ SQL Server โดยทั่วไปเราทดสอบความเร็วในการอ่านข้อมูลจากหน่วยเก็บข้อมูลและประมวลผลข้อมูลนั้นจาก cold-cache (เช่นบัฟเฟอร์ของ SQL Server ว่างเปล่า) ในบางครั้งมันสมเหตุสมผลที่จะทำการทดสอบทั้งหมดด้วย warm-cache แต่คุณต้องเข้าใกล้การทดสอบของคุณอย่างชัดเจนโดยคำนึงถึงสิ่งนั้น

สำหรับการทดสอบ cold-cache คุณจำเป็นต้องเรียกใช้CHECKPOINTและDBCC DROPCLEANBUFFERSก่อนการทดสอบแต่ละครั้ง

สำหรับการทดสอบที่คุณถามเกี่ยวกับคำถามของคุณฉันได้สร้างแบบทดสอบดังต่อไปนี้:

IF COALESCE(OBJECT_ID('tempdb..#SomeTest'), 0) <> 0
BEGIN
    DROP TABLE #SomeTest;
END
CREATE TABLE #SomeTest
(
    TestID INT NOT NULL
        PRIMARY KEY 
        IDENTITY(1,1)
    , A INT NOT NULL
    , B FLOAT NOT NULL
    , C MONEY NOT NULL
    , D BIGINT NOT NULL
);

INSERT INTO #SomeTest (A, B, C, D)
SELECT o1.object_id, o2.object_id, o3.object_id, o4.object_id
FROM sys.objects o1
    , sys.objects o2
    , sys.objects o3
    , sys.objects o4;

SELECT COUNT(1) 
FROM #SomeTest;

ส่งคืนจำนวน 260,144,641 ในเครื่องของฉัน

เพื่อทดสอบวิธี "การเพิ่ม" ฉันเรียกใช้:

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE (st.A + st.B + st.C + st.D) = 0;
GO
SET STATISTICS IO, TIME OFF;

แท็บข้อความแสดง:

ตาราง '#SomeTest' จำนวนการสแกน 3, การอ่านเชิงตรรกะ 1322661, การอ่านทางกายภาพ 0, การอ่านล่วงหน้าอ่าน 1313877, lob การอ่านเชิงตรรกะ 0, lob การอ่านทางกายภาพ 0, lob การอ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ SQL Server: เวลา CPU = 49047 ms, เวลาที่ผ่านไป = 173451 ms

สำหรับการทดสอบ "คอลัมน์แยก":

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE st.A = 0
    AND st.B = 0
    AND st.C = 0
    AND st.D = 0;
GO

SET STATISTICS IO, TIME OFF;

อีกครั้งจากแท็บข้อความ:

ตาราง '#SomeTest' จำนวนการสแกน 3, อ่านโลจิคัล 1322661, อ่านฟิสิคัล 0, อ่านล่วงหน้าอ่าน 1322661, ล๊อบอ่านตรรกะ 0, lob อ่านฟิสิคัล 0, lob อ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ SQL Server: เวลา CPU = 8938 ms, เวลาที่ผ่านไป = 162581 ms

จากสถิติด้านบนคุณจะเห็นตัวแปรที่สองโดยคอลัมน์แบบแยกเมื่อเทียบกับ 0 เวลาที่ผ่านไปจะสั้นลงประมาณ 10 วินาทีและเวลา CPU จะลดลงประมาณ 6 เท่า ระยะเวลานานในการทดสอบของฉันข้างต้นส่วนใหญ่เป็นผลมาจากการอ่านแถวจำนวนมากจากดิสก์ หากคุณลดจำนวนแถวลงเหลือ 3 ล้านคุณจะเห็นว่าอัตราส่วนยังคงเท่าเดิม แต่เวลาที่ผ่านไปลดลงอย่างเห็นได้ชัดเนื่องจากดิสก์ I / O มีผลกระทบน้อยกว่ามาก

ด้วยวิธีการ "เพิ่มเติม":

ตาราง '#SomeTest' จำนวนการสแกน 3, การอ่านโลจิคัล 15255, การอ่านทางกายภาพ 0, การอ่านล่วงหน้าอ่าน 0, lob โลจิคัลการอ่าน 0, lob การอ่านทางกายภาพ 0, lob การอ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ SQL Server: เวลา CPU = 499 ms, เวลาที่ผ่านไป = 256 ms

ด้วยวิธี "คอลัมน์แยก":

ตาราง '#SomeTest' จำนวนการสแกน 3, การอ่านโลจิคัล 15255, การอ่านทางกายภาพ 0, การอ่านล่วงหน้าอ่าน 0, lob โลจิคัลการอ่าน 0, lob การอ่านทางกายภาพ 0, lob การอ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ SQL Server: เวลา CPU = 94 ms, เวลาที่ผ่านไป = 53 ms

อะไรจะสร้างความแตกต่างที่ยิ่งใหญ่สำหรับการทดสอบนี้ ดัชนีที่เหมาะสมเช่น:

CREATE INDEX IX_SomeTest ON #SomeTest(A, B, C, D);

วิธีการ "เพิ่มเติม":

ตาราง '#SomeTest' จำนวนการสแกน 3, อ่านลอจิคัล 14235, อ่านฟิสิคัล 0, อ่านล่วงหน้าอ่าน 0, lob อ่านโลจิคัล 0, lob อ่านฟิสิคัล 0, lob อ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ SQL Server: เวลา CPU = 546 ms, เวลาที่ผ่านไป = 314 ms

วิธีการ "คอลัมน์แยก":

ตาราง '#SomeTest' จำนวนการสแกน 1, การอ่านเชิงตรรกะ 3, การอ่านทางกายภาพ 0, การอ่านล่วงหน้าอ่าน 0, lob ตรรกะอ่าน 0, lob การอ่านทางกายภาพ 0, lob การอ่านล่วงหน้าอ่าน 0

เวลาดำเนินการของ SQL Server: เวลา CPU = 0 ms, เวลาที่ผ่านไป = 0 ms

แผนการดำเนินการสำหรับแต่ละแบบสอบถาม (ด้วยดัชนีด้านบนแทน) ค่อนข้างบอก

วิธีการ "เพิ่มเติม" ซึ่งจะต้องทำการสแกนดัชนีทั้งหมด:

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

และวิธีการ "คอลัมน์แยก" ซึ่งสามารถค้นหาในแถวแรกของดัชนีที่คอลัมน์ดัชนีนำAเป็นศูนย์:

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


24

สมมติว่าคุณมีดัชนีใน A, B, C และ D ก็สามารถกรองได้เช่นกัน

นี่มีแนวโน้มที่จะใช้ดัชนีมากกว่านั้น

Where A=0 and B=0 and C=0 and D=0

ในข่าวอื่น ๆ ถ้า A คือ -1 และ B คือ 1 A+B=0จะเป็นจริง แต่A=0 and B=0เป็นเท็จ


7

(โปรดทราบว่าคำตอบนี้ถูกส่งก่อนการทดสอบใด ๆ ที่ระบุไว้ในคำถาม: ข้อความของคำถามสิ้นสุดเหนือส่วนผลการทดสอบ )

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

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


3

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

การแยก (OR) โดยทั่วไปทำงานได้แย่กว่า conjunctions (AND) แต่ถึงแม้ว่าคุณจะมีแบบสอบถามที่มีการแตกหักฉันจะนำเงินของฉันไปไว้ในครั้งแรก


2

นี่เป็นคำถามง่าย ๆ

ไม่มันไม่ใช่. คำถาม (ชนิดของ) นี้คือสิ่งที่ทำให้เกิดปัญหากับ DBAs และผู้พัฒนาซอฟต์แวร์จำนวนมากในแต่ละวันและเป็นเพียงเล็กน้อยเท่านั้น

ฉันไม่สามารถหาคำตอบได้

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

จากนั้นก็สามารถขึ้นอยู่กับรายละเอียดเล็ก ๆ จำนวนเท่าใดก็ได้ตัวอย่างเช่นวิธีที่ DB เก็บข้อมูลของคุณหากคุณมี sub-selects / joins ที่สร้างความสับสนให้กับเครื่องมือเพิ่มประสิทธิภาพแผนเป็นต้นเครื่องมือเพิ่มประสิทธิภาพอาจให้แผนการดำเนินการที่แตกต่างกัน คุณมีกี่แถว ...

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


0

สิ่งนี้อาจชัดเจน แต่ถ้าคอลัมน์เป็นINTเช่นนั้นก็a+b+cอาจเท่ากับศูนย์แม้ว่าจะไม่มีศูนย์ใดเลยก็ตาม คุณกำลังทดสอบสองสิ่งที่แตกต่าง!


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