T-SQL'de CAST kullanarak performans isabeti


12

Belirtilen alanlar için genel olarak SQL koşullu ifadeleri yayan bir SQL üretecimiz var (bu tartışma amacıyla: olarak etiketleyeceğiz myField).

Eğer myFieldtiptedir NVARCHAR, böylece gibi bir dize karşı söz konusu alanın bir karşılaştırma yapabilirsiniz: myField = 'foo'.

Ancak, bu tür alanlar için çalışmaz NTEXT. Böylece, bir alçı ile karşılaştırma yapmak zorunda: CAST(myField as NVARCHAR(MAX)) = 'foo'. Bu aslında myFieldtip NVARCHARveya ise işe yarayacaktır NTEXT.

Zaten tipte olan bir alanda yukarıda belirtilen oyuncu kadrosunun performans artışı nedir NVARCHAR? Umudum, SQL Server'ın dinamik olarak tanımak için yeterince akıllı ( myFieldzaten NVARCHARetkin CASTbir no-op dönüşüyor ) olmasıdır.


Bu soruyu bulan herkes için kısa bir not: NTEXT (ve METİN ve GÖRÜNTÜ) resmen kullanımdan kaldırılmıştır ve SQL Server'ın gelecekteki bazı sürümlerinde kaldırılması nedeniyle (IIRC, yine de SQL1014'te çalışmaktadır), bu nedenle NVARCHR (MAX) kullanmalısınız (veya bunun yerine VARCHAR (MAX) veya VARBINARY (MAX)). NTEXT sütununu bu örnekte bir NVARCHAR (MAX) ile değiştirmek, karşılaştırma doğrudan bu türle yapılabileceğinden ve burada ve başka yerlerde de başka potansiyel verimlilik kazanımları olduğu için döküm ihtiyacını ortadan kaldıracaktır. Maalesef bir * (MAX) sütunu dizine ekleyemezsiniz, ancak bir METİN / NTEXT sütunu da oluşturamazsınız.
David Spillett

Yanıtlar:


12

Eğer kolonun kadrosu tam olarak aynı veri tipine ve uzunluğa sahipse ve arama yüklemi gerçek ise, onu gerçekten göz ardı eder ya da bir op-op olarak kabul etmez ve bir indeks eşitliği arar.

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

Sütunun dökümü aynı veri tipine ancak daha uzun bir uzunluğa ve arama yüklemi bir dize değişmeziyse, bir dizin taramasına neden olur. Bundan açıkça kaçınılmalıdır.

Sütunun dökümü aynı veri tipine ve aynı veya daha büyük uzunluğa ve arama yüklemi yerel bir değişkense, yürütme planına bir hesaplama skaler operatörü ekler. Bu GetRangeThroughConvertbir aralık çağırır ve çıkarır.

Bu aralık bir dizin araması yapmak için kullanılır ve oldukça verimli görünür

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

Test Kodu

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

Genel olarak, CASTirade performansı öldürecek çünkü Martin Smith'in son örneğinin gösterdiği gibi herhangi bir endeks kullanımını geçersiz kılıyor. nvarchar(max)Farklı bir uzunluğa veya farklı bir uzunluğa yayın yapmak farklı bir veri türü anlamına gelir: her nvarcharşeyin alakasız olması.

Bunun da ötesinde, karşılaştırmanın sağ tarafındaki veri türü de önemlidir. Farklı bir uzunluğa sahip bir yerel değişken veya parametre ise, bir taraf CAST2 veri tipinin en genişine dolaylı olarak sahip olacaktır (bkz. Veri tipi önceliği ).

Eğer bir general varsa Temelde, CASTiçin nvarchar(max)o şeyleri bollix olacaktır. Her ntextşeyi eklemeden önce kullanımını düzeltmeyi düşünürdüm CAST.

Dönüşüm sorgu planında gösterilmeyebilir. Bkz Paul White'ın blog makalesinde


2

Sadece bir not, Datecreated'in datetime olduğu yerde yayınlamak

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

Dizinler varsa SQL'in dizinleri kullanma yeteneğini bozmaz ve yoksa dizin eksik bir dizinin günlüğe kaydedilmesine neden olabilir.

Yayın yapmak Benzer şekilde, intkarşı tinyintya bigintkadar intvb dökme fonksiyonu optimiser dağıtım işlemi 2 karşılaştırılabilir veri türleri sıralama düzenini değiştirmek olmadığını bilir EĞER endeksleri kullanmasını SQL durmaz.

İşte Adventureworks2008R2'yi kullanarak gerçek planı çalıştırabileceğiniz ve görüntüleyebileceğiniz bir sürü test

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
tarih olarak yayınlama, bir dizin araması yapabilir ancak yine de, yayın yapmadan aralıklı arama olarak ifade etmeyle karşılaştırıldığında sorun yaşar.
Martin Smith
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.