ประสิทธิภาพการทำงานยอดเยี่ยมใช้ CAST ใน T-SQL


12

เรามีเครื่องกำเนิดไฟฟ้า SQL ที่ส่งเสียง SQL งบเงื่อนไขทั่วไปสำหรับเขตข้อมูลที่ระบุไว้ (ซึ่งเพื่อประโยชน์ของการอภิปราย: เราจะติดป้ายว่าเป็นmyField)

ถ้าmyFieldเป็นประเภทที่เราสามารถทำได้เปรียบเทียบข้อมูลดังกล่าวกับสายเช่นเพื่อให้เป็น:NVARCHARmyField = 'foo'

NTEXTแต่นี้ไม่ทำงานสำหรับเขตข้อมูลประเภท CAST(myField as NVARCHAR(MAX)) = 'foo'ดังนั้นเราจึงต้องทำเปรียบเทียบกับหล่อ: ประสงค์ในการทำงานความจริงเรื่องนี้ถ้าmyFieldเป็นประเภทหรือNVARCHARNTEXT

อะไรคือผลงานยอดเยี่ยมของการทำนักแสดงดังกล่าวในสนามที่มีประเภทอยู่แล้วNVARCHAR ? ฉันหวังว่า SQL Server เป็นพอสมาร์ทที่จะรับรู้แบบไดนามิกที่myFieldมีอยู่แล้วจากประเภทNVARCHAR(ประสิทธิภาพการเปลี่ยนCASTเป็นไม่มี-op)


บันทึกย่อแบบด่วนสำหรับทุกคนที่พบคำถามนี้: NTEXT (และ TEXT และ IMAGE) เลิกใช้งานอย่างเป็นทางการและเนื่องจากจะถูกลบใน SQL Server รุ่นอนาคตบางรุ่น (แม้ว่า IIRC จะยังคงใช้งานได้ใน SQL1014) ดังนั้นคุณควรใช้ NVARCHR (MAX) (หรือ VARCHAR (สูงสุด) หรือ VARBINARY (สูงสุด)) แทน การแทนที่คอลัมน์ NTEXT ด้วย NVARCHAR (MAX) หนึ่งรายการในอินสแตนซ์นี้จะลบความต้องการนักแสดงเนื่องจากการเปรียบเทียบสามารถทำได้โดยตรงกับประเภทนั้นและมีประสิทธิภาพที่เป็นไปได้อื่น ๆ ที่นี่และที่อื่น ๆ ด้วย น่าเสียดายที่คุณไม่สามารถสร้างดัชนีคอลัมน์ * (MAX) ได้ แต่คุณไม่สามารถสร้างดัชนี TEXT / NTEXT ได้
David Spillett

คำตอบ:


12

หากการโยนคอลัมน์เป็นประเภทข้อมูลและความยาวเท่ากันและการค้นหาคำกริยาเป็นสัญพจน์มันดูเหมือนจะไม่สนใจมันจริง ๆ หรือมองว่ามันเป็น no-op และดัชนีค้นหาความเท่าเทียมกัน

Seek Keys[1]: Prefix: [tempdb].[dbo].[#test].name = Scalar Operator(N'rpc')

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

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

ช่วงนี้ใช้เพื่อค้นหาดัชนีและดูมีประสิทธิภาพทีเดียว

Seek Keys[1]: 
Start: [tempdb].[dbo].[#test].name > Scalar Operator([Expr1006]), 
End: [tempdb].[dbo].[#test].name < Scalar Operator([Expr1007])

รหัสการทดสอบ

SELECT *
 INTO #test
  FROM [master].[dbo].[spt_values]

CREATE NONCLUSTERED INDEX [ixname] ON #test
(
    [name] ASC
)

DECLARE @name NVARCHAR(MAX)

SET @name = 'rpc'

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(35))= @name --Cast the same and local variable

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(MAX))=@name --Cast to longer and local variable

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(35))='rpc' --Cast the same and literal

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(MAX))='rpc' --Cast to longer and literal

6

โดยทั่วไปแล้ว the CASTwill kill performance เนื่องจากจะทำให้การใช้ดัชนีค้นหาเป็นโมฆะดังตัวอย่างล่าสุดของ Martin Smith การส่งไปยังnvarchar(max)หรือตามความยาวแตกต่างกันหมายถึงประเภทข้อมูลที่แตกต่าง: ความจริงมันคือทั้งหมดที่nvarcharไม่เกี่ยวข้อง

ยิ่งไปกว่านั้นประเภทข้อมูลของด้านขวามือของการเปรียบเทียบก็มีความสำคัญเช่นกัน หากเป็นตัวแปรในตัวเครื่องหรือพารามิเตอร์ที่มีความยาวต่างกันด้านใดด้านหนึ่งจะมีCASTความกว้างมากที่สุดของ 2 ประเภทข้อมูล (ดูประเภทข้อมูลที่มีมาก่อน )

โดยทั่วไปถ้าคุณมีทั่วไปCASTเพื่อnvarchar(max)จะ bollix สิ่งขึ้น ฉันจะพิจารณาแก้ไขการใช้ntextก่อนที่จะเพิ่มCASTไปหมด

การแปลงอาจไม่แสดงในแผนคิวรี ดูบทความบล็อกของ Paul White


2

แค่โน้ตการร่ายแบบนี้ที่ Datecreated คือ datetime

 Cast (Datecreated as date) = cast(@MydatetimeValue as date)

ไม่ทำลายความสามารถของ SQL ในการใช้ดัชนีหากมีดัชนีและหากไม่มีอยู่อาจส่งผลให้บันทึกดัชนีที่ขาดหายไป

ในทำนองเดียวกันเมื่อหล่อจากintไปtinyintหรือbigintไปintฯลฯ ฟังก์ชั่นโยนไม่หยุด SQL จากการใช้ดัชนี IF เพิ่มประสิทธิภาพรู้ว่าการดำเนินการหล่อไม่เปลี่ยนลำดับการจัดเรียงของ 2 ประเภทข้อมูลเทียบเคียง

ต่อไปนี้เป็นชุดการทดสอบที่คุณสามารถเรียกใช้และดูแผนที่จริงโดยใช้ Adventureworks2008R2

select count(*) from Sales.SalesOrderDetail where SalesOrderID = 8 --1
select top 10 * from Sales.SalesOrderDetail where cast(SalesOrderID as tinyint) = 8  --2
select top 10 * from Sales.SalesOrderDetail where cast(SalesOrderID as bigint) = 8  --3
select top 10 SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate  as date) = '19780322' --4
select top 10 SalesOrderID from Sales.SalesOrderDetail where convert(date,ModifiedDate) = '19780322'  --5
select top 10 SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate as varchar(20)) = '1978'  --6 -- THIS WILL NOT USE INDEX
select  SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate  as date) between '19780101' and '19780109'  --7

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