Performa hit menggunakan CAST di T-SQL

12

Kami memiliki generator SQL yang memancarkan pernyataan kondisional SQL secara umum untuk bidang tertentu (yang untuk diskusi: kami akan beri label sebagai myField).

Jika myFieldadalah tipe NVARCHAR, kita bisa melakukan perbandingan dari lapangan mengatakan terhadap string seperti: myField = 'foo'.

Namun, ini tidak berfungsi untuk bidang tipe NTEXT. Dengan demikian, kita harus melakukan perbandingan dengan gips: CAST(myField as NVARCHAR(MAX)) = 'foo'. Ini sebenarnya akan bekerja jika myFieldbertipe NVARCHARatau NTEXT.

Apa kinerja yang berhasil dilakukan oleh para pemeran yang disebutkan di atas pada bidang yang sudah bertipe NVARCHAR? Harapan saya adalah bahwa SQL Server cukup pintar untuk secara dinamis mengenali yang myFieldsudah tipe NVARCHAR(secara efektif mengubah CASTmenjadi no-op).

Paul White 9
sumber
Catatan singkat untuk siapa saja yang menemukan pertanyaan ini: NTEXT (dan TEXT dan IMAGE) secara resmi ditinggalkan dan karena akan dihapus dalam beberapa versi SQL Server di masa depan (meskipun IIRC mereka masih bekerja di SQL1014), jadi Anda harus menggunakan NVARCHR (MAX) (atau VARCHAR (MAX) atau VARBINARY (MAX)) sebagai gantinya. Mengganti kolom NTEXT dengan NVARCHAR (MAX) yang dalam contoh ini akan menghilangkan kebutuhan untuk pemain karena perbandingan dapat dilakukan secara langsung dengan jenis itu, dan ada keuntungan efisiensi potensial lainnya di sini dan di tempat lain juga. Sayangnya Anda tidak dapat mengindeks kolom * (MAX), tetapi Anda juga tidak bisa menggunakan TEXT / NTEXT.
David Spillett

Jawaban:

12

Jika para pemeran kolom memiliki tipe dan panjang data yang sama persis dan predikat pencariannya adalah literal, hal itu memang tampaknya mengabaikannya atau memperlakukannya sebagai no-op dan melakukan indeks mencari kesetaraan.

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

Jika cetakan kolom adalah untuk tipe data yang sama tetapi panjang yang lebih besar dan predikat pencarian adalah string literal, hal itu menyebabkan pemindaian indeks. Ini jelas harus dihindari.

Jika para pemain kolom adalah untuk tipe data yang sama dan panjang yang sama atau lebih besar dan predikat pencarian adalah variabel lokal itu menambahkan operator skalar komputasi ke rencana eksekusi. Ini panggilan GetRangeThroughConvertdan output jangkauan.

Rentang ini digunakan untuk melakukan pencarian indeks dan tampaknya cukup efisien

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

Kode Pengujian

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
Martin Smith
sumber
6

Secara umum, ini CASTakan mematikan kinerja karena membatalkan penggunaan indeks seperti yang ditunjukkan contoh terakhir Martin Smith. Casting ke nvarchar(max)atau ke panjang yang berbeda berarti tipe data yang berbeda: fakta itu semua nvarchartidak relevan.

Di atas itu semua, tipe data dari sisi kanan membandingkan juga. Jika itu adalah variabel lokal atau parameter dengan panjang yang berbeda, maka satu sisi akan secara implisit CASTmenjadi yang terluas dari 2 tipe data (lihat prioritas tipe data ).

Pada dasarnya, jika Anda memiliki jenderal CASTuntuk nvarchar(max)itu akan bollix segalanya. Saya akan mempertimbangkan untuk memperbaiki penggunaan ntextsebelum saya menambahkan CASTsemuanya.

Konversi tidak dapat ditampilkan dalam paket kueri. Lihat artikel blog Paul White

gbn
sumber
2

Sekedar catatan, Casting seperti ini di mana Datecreated adalah datetime

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

Tidak merusak kemampuan SQL untuk menggunakan indeks jika indeks ada, dan jika tidak ada, dapat mengakibatkan masuknya indeks yang hilang.

Demikian pula, ketika melakukan casting dari intke tinyintatau bigintke intdll, fungsi cast tidak menghentikan SQL dari menggunakan indeks JIKA optimizer tahu bahwa operasi cast tidak mengubah urutan sortir dari 2 tipe data yang sebanding.

Berikut adalah banyak tes yang dapat Anda jalankan dan lihat paket aktual menggunakan 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
Doran Mackay
sumber
1
pemain sebagai tanggal dapat melakukan pencarian indeks tetapi masih memiliki masalah dibandingkan dengan menyatakan sebagai rentang pencarian tanpa pemain.
Martin Smith