มีวิธีการตรวจสอบว่างในตัวแปรในส่วนคำสั่ง WHERE ที่เกิดขึ้นเพียงครั้งเดียวหรือไม่?


12

ฉันมีแบบสอบถามในตารางขนาดใหญ่ที่มีลักษณะเช่นนี้:

declare @myIdParam int = 1

select * 
from myTable
where (@myIdParam is null or myTable.Id = @myIdParam)

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

อย่างมีประสิทธิภาพหาก @myIdParam เป็นโมฆะเราไม่ต้องการ จำกัด ผลลัพธ์โดยใช้พารามิเตอร์นี้

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

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

มีวิธีการเพิ่มประสิทธิภาพนี้เพื่อตรวจสอบจะทำเพียงครั้งเดียวที่รันไทม์?


1
ดูคำตอบนี้: stackoverflow.com/questions/3415582/… tl; dr useOPTION(RECOMPILE)
vercelli

@vercelli นี่ทำกลอุบาย การพิจารณาคำถามนี้เป็นเรื่องเกี่ยวกับพารามิเตอร์ทางเลือกจริง ๆ ฉันขอบอกว่าเป็นสิ่งที่ซ้ำซ้อนกับที่คุณเชื่อมโยง
Mystagogue

อาจเป็น แต่มันเป็นโพสต์เมื่อ 6 ปีที่แล้ว อาจใช้ SqlServer 2014 หรือ 2016 อาจมีวิธีการใหม่ (ผมทดสอบบน 2014 โดยไม่ต้องคอมไพล์และเอาตลอดไป)
Vercelli

เนื่องจากแบบสอบถามที่แท้จริงของคุณมีพารามิเตอร์ที่เป็นตัวเลือก SQL แบบไดนามิกจะให้ประสิทธิภาพที่ดีที่สุด ดูsommarskog.se/dyn-search.htmlสำหรับบทความที่ละเอียดเกี่ยวกับหัวเรื่อง
Dan Guzman

@DanGuzman ใช้กับ RECOMPILE ตามที่ระบุไว้ในคำถามที่เชื่อมโยง vercelli ลดเวลาในการค้นหาจากเพียงไม่กี่นาทีถึงทันทีในทางปฏิบัติกับเกณฑ์การคัดเลือกสูง ฉันคิดว่านี่เป็นตัวเลือกที่ดีที่สุดสำหรับการปรับสมดุลประสิทธิภาพและความสามารถในการอ่าน
Mystagogue

คำตอบ:


8

วิธีหนึ่งคือการใช้ไดนามิก SQL โดยใช้การตรวจสอบโมฆะเพื่อเลือกเพิ่มส่วนของ where clause

declare @myIdParam int = 1
declare @vc_dynamicsql varchar(max)

set @vc_dynamicsql = 'select * from myTable where 1=1'

if @myIdParam is not null
    set @vc_dynamicsql = @vc_dynamicsql + ' and  myTable.Id = @myIdParam'

EXECUTE sp_executesql @vc_dynamicsql

2
ฉันไม่ชอบที่จะทำสิ่งนี้ แต่มันเป็นทางออก ความหวังของฉันคือมีคนมาด้วยดีกว่ามาก
Mystagogue

1
นี่เป็นวิธีที่ดีที่สุดในการจัดการคำค้นหาระดับนี้ คำตอบ StackOverflow อ้างถึงโดย @vercelliมีการอ้างอิงที่ดีสำหรับการทำเช่นนี้
Max Vernon

นี่คือวิธีที่ดีที่สุด แต่ผมไม่แจ้งให้ทราบล่วงหน้าว่า @params พารามิเตอร์sp_ExecuteSQLจะหายไปและความต้องการของพารามิเตอร์ที่จะเป็น@vc_dynamicsql NVARCHAR
James Anderson

4

ทุกครั้งที่คุณใส่ฟังก์ชันรอบ ๆ คอลัมน์ ISNULL (@var, table.col) 'ตัวอย่างเช่นคุณลบความสามารถของ SQL ในการใช้ดัชนี นี่เป็นตัวเลือกที่มีประสิทธิภาพดีที่สุดหากคุณต้องการเก็บไว้ในแบบสอบถามเดียว

@var IS NULL or @var = table.col

มิฉะนั้นคุณมีสองตัวเลือก อย่างแรกคือ SQL แบบไดนามิกและคำตอบของ @ Mystagogue ก็เพียงพอแล้วมิฉะนั้นคุณสามารถใส่แบบสอบถามสองแบบดังนี้:

IF @var is NULL
     SELECT * FROM table
ELSE
     SELECT * FROM table WHERE @var = col

ในทั้งรูปแบบนี้และ SQL แบบไดนามิกคุณจะได้รับแผนแบบสอบถามที่แตกต่างกันสำหรับแต่ละแบบสอบถาม (ซึ่งอาจจะให้ประสิทธิภาพที่ดีกว่า)


SQL ในคำถามไม่ได้ใช้ ISNULL หรือฟังก์ชั่นอื่น ๆ
Mystagogue

@MystagogueI ฉันอ้างอิงคำตอบที่ถูกลบแล้ว
Kenneth Fisher

0

คุณสามารถ:

declare @myIdParam int = 1;

select *
from myTable
where nullif(@myIdParam, myTable.Id) is null;

เก็บไว้ในใจ แต่ที่ฟังก์ชั่นเป็นหลักมากกว่าเสื้อคลุมnullif() caseไม่ใช่กระสุนเงินที่กำจัดได้อย่างน่าอัศจรรย์ORและทำให้การค้นหาเร็วขึ้น


ใช้ฟังก์ชั่นในส่วนคำสั่งที่มีผลกระทบเชิงลบต่อประสิทธิภาพเพราะป้องกันการใช้ดัชนี (หรือฉันเคยได้ยิน)
Mystagogue

@Mystagogue ใช่ - โดยปกติจะทำให้เงื่อนไขการค้นหาไม่ใช่ SARGable อนิจจานี่เป็นวิธีเดียวที่ฉันรู้วิธีที่จะตอบคำถามของคุณโดยไม่ต้องหันไปใช้ SQL แบบไดนามิกหรือหลาย ๆUNIONวิธี เมื่อฉันมีงานที่แน่นอนนี้ฉันเลือก SQL แบบไดนามิก
Roger Wolf
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.