SQL Server A <> B'yi A <B VEYA A> B'ye böler, B deterministik değilse garip sonuçlar verir.


26

SQL Server ile ilginç bir sorunla karşılaştık. Aşağıdaki repro örneğini göz önünde bulundurun:

CREATE TABLE #test (s_guid uniqueidentifier PRIMARY KEY);
INSERT INTO #test (s_guid) VALUES ('7E28EFF8-A80A-45E4-BFE0-C13989D69618');

SELECT s_guid FROM #test
WHERE s_guid = '7E28EFF8-A80A-45E4-BFE0-C13989D69618'
  AND s_guid <> NEWID();

DROP TABLE #test;

keman

Lütfen bir anlığına s_guid <> NEWID()durumun tamamen işe yaramaz göründüğünü unutmayın - bu sadece minimal bir repro örneğidir. NEWID()Belirli bir sabit değeri eşleştirme olasılığı son derece küçük olduğundan, her zaman TRUE olarak değerlendirmelidir.

Ama öyle değil. Bu sorguyu çalıştırmak genellikle 1 satır döndürür, ancak bazen (oldukça sık, 10'dan 1'den fazla zaman) 0 satır döndürür. Sistemimde SQL Server 2008 ile çoğalttım ve yukarıdaki bağlantılı kemanla çevrimiçi olarak yeniden oluşturabilirsiniz (SQL Server 2014).

Yürütme planına bakmak, sorgu analizcisinin görünüşte koşulu aşağıdakilere ayırdığını ortaya koyuyor s_guid < NEWID() OR s_guid > NEWID():

sorgu planı ekran görüntüsü

... bu neden bazen başarısız olduğunu açıklar (eğer ilk üretilen ID daha küçük ve ikincisi verilen ID'den daha büyükse).

SQL Server değerlendirmek için izin mi A <> Bolduğu A < B OR A > Bifadelerden birinin belirli olmayan olsa bile,? Evet ise, nerede belgelenmiştir? Yoksa bir böcek mi bulduk?

İlginç bir şekilde, AND NOT (s_guid = NEWID())aynı uygulama planını (ve aynı rastgele sonucu) verir.

Bir geliştirici isteğe bağlı olarak belirli bir satırı dışlamak istediğinde ve kullandığında, bu sorunu bulduk:

s_guid <> ISNULL(@someParameter, NEWID())

Şunun için "kısayol" olarak:

(@someParameter IS NULL OR s_guid <> @someParameter)

Belgeleme ve / veya bir hatanın onayını arıyorum. Kod, alakalı olanların tümü değildir; bu nedenle geçici çözümler gerekli değildir.


Yanıtlar:


22

SQL Server değerlendirmek için izin mi A <> Bolduğu A < B OR A > Bifadelerden birinin belirli olmayan olsa bile,?

Bu biraz tartışmalı bir konu ve cevap nitelikli bir "evet".

Farkında olduğum en iyi tartışma, Itzik Ben-Gan'ın Connect hata raporunda , NEWID ve Tablo İfadeleri ile Hata mesajı düzeltmeyecek şekilde yanıtlandı . Connect o zamandan beri emekli oldu, bu yüzden oradaki bağlantı bir web arşivi. Maalesef, Connect'in ölümüyle birçok faydalı materyal kaybedildi (ya da bulması zorlaştı). Her neyse, Microsoft'tan Jim Hogg'dan en yararlı alıntılar:

Bu konunun tam ortasına isabet ediyor - bir programın anlamını değiştirmek için optimizasyona izin veriliyor mu? Yani: eğer bir program belirli cevaplar verir, fakat yavaş çalışırsa, bir Sorgu Optimize Edici'nin bu programı daha hızlı çalıştırmasını sağlamak meşru mudur, ancak verilen sonuçları da değiştirebilir mi?

"HAYIR!" Diye bağırmadan önce (benim kendi kişisel eğilimim de :-) düşünün: iyi haber, vakaların% 99'unda cevapların aynı olduğu. Yani Sorgu Optimizasyonu açık bir kazançtır. Kötü haberse, eğer sorgu yan etki kodunu içeriyorsa, o zaman farklı planlar CAN gerçekten farklı sonuçlar verir. Ve NEWID (), farkı ortaya çıkaran böyle bir yan etkileyici (deterministik olmayan) 'işlevdir'. [Eğer deney yaparsanız, başkalarını tasarlayabilirsiniz - örneğin, AND cümlelerinin kısa devre değerlendirmesi: ikinci cümleyi bir aritmetik sıfıra bölme - atma - birinci cümleden ÖNCE ikinci maddeyi uygulayabilir] Bu, yansıtır. Craig'in bu konudaki herhangi bir yerindeki açıklaması, SqlServer'ın skaler operatörleri çalıştırıldığında garanti etmediğini açıklıyor.

Öyleyse, bir seçeneğimiz var: deterministik olmayan (yan-etkileyici) kodun varlığında belirli bir davranışı garanti etmek istiyorsak - ki JOIN'lerin sonuçları, örneğin, iç içe geçmiş bir uygulamanın anlamını takip eder - o zaman biz UC'nin işaret ettiği gibi, bu davranışı zorlamak için uygun SEÇENEKLERİ kullanabilir. Ancak sonuçta ortaya çıkan kod yavaş çalışacaktır - bu, aslında, Sorgu Optimize Edici'yi azaltmanın maliyetidir.

Tüm bunlar, Sorgu Optimize Edici’yi NEWID () için "beklendiği gibi" davranışı yönünde hareket ettiriyoruz - "beklendiği gibi sonuçlar" için performans düşüyor.

Zaman içinde bu konuda davranış değişikliğinin bir örneği NULLIF, RAND () gibi deterministik olmayan fonksiyonlarla yanlış çalışır . COALESCEBeklenmeyen sonuçlar üretebilen ve aynı zamanda yavaş yavaş ele alınan bir alt sorguyla birlikte kullanılan başka benzer durumlar da vardır .

Jim devam ediyor:

Döngüyü kapatıyorum. . . Bu soruyu Dev ekibi ile tartıştım. Ve nihayetinde, aşağıdaki sebeplerden dolayı mevcut davranışı değiştirmemeye karar verdik:

1) Optimize edici, skalar fonksiyonların zamanlamasını veya uygulama sayısını garanti etmez. Bu uzun vadeli bir inanç olduğunu. Bu, temel optimizasyonun optimize ediciye sorgu planı yürütmede önemli iyileştirmeler sağlaması için yeterli özgürlüğü sağlar.

2) Bu "satır başına bir davranış" yeni bir sorun değil, ancak geniş bir şekilde tartışılmamış. Yukon sürümünde davranışını değiştirmeye başladık. Ancak, her durumda, tam olarak ne anlama geldiğini tam olarak tespit etmek oldukça zordur! Örneğin, nihai sonuç için 'yolda' hesaplanan geçici satırlar için geçerli midir? - bu durumda açıkça seçilen plana bağlıdır. Yoksa yalnızca sonuçta tamamlanan sonuçta görünecek olan satırlar için de geçerli midir? - Burada katılıyorum, çünkü katılıyorum!

3) Daha önce de belirttiğim gibi, varsayılan olarak "performansı optimize etmek" - varsayılan olarak% 99 için iyidir. Sonuçları değiştirebileceği vakaların% 1'i, NEWID gibi yan etkileyici 'işlevler' açısından oldukça kolay anlaşılır ve 'düzeltilmesi' kolaydır (bunun sonucunda işlem harikası). Yeniden "performansı optimize etmek" için varsayılan bu, uzun süredir kurulmuş ve kabul edilmiştir. (Evet, derleyiciler tarafından geleneksel programlama dilleri için seçilen duruş değil, öyle olsun).

Yani, önerilerimiz:

a) Garanti edilmeyen zamanlama ve infaz sayısı semantiğine güvenmekten kaçının. b) NEWID () ifadesini tablo ifadelerinin derinliklerinde kullanmaktan kaçının. c) Belirli bir davranışı zorlamak için OPTION seçeneğini kullanın (işlem perf)

Umarım bu açıklama bu hatayı "düzeltmeyecek" olarak kapatma nedenlerimizi netleştirmemize yardımcı olur.


İlginçtir, AND NOT (s_guid = NEWID())aynı yürütme planını verir

Bu, sorgu derlemesi sırasında çok erken olan normalleşmenin bir sonucudur. Her iki ifade de tam olarak aynı normalize formda derlenir, böylece aynı uygulama planı üretilir.


Bu durumda, sorundan kaçınıyor gibi görünen belirli bir planı zorlamak istiyorsak, WITH (FORCESCAN) kullanabiliriz. Kesin olarak, sorguyu çalıştırmadan önce NEWID () sonucunu saklamak için bir değişken kullanmalıyız.
Razvan Socol

11

Bu burada belgelenmiştir (tür):

Bir sorguda belirtilen bir işlevin gerçekte çalıştırılma sayısı, optimize edici tarafından oluşturulan yürütme planları arasında değişebilir. Örnek, bir WHERE yan tümcesinde bir alt sorgu tarafından çağrılan bir işlevdir. Alt sorgunun ve işlevinin çalıştırılma sayısı, en iyileştirici tarafından seçilen farklı erişim yollarına göre değişebilir.

Kullanıcı tanımlı fonksiyonlar

Bu, sorgu planının NEWID () yöntemini birden çok kez uygulayacağı ve sonucu değiştireceği tek sorgu formu değildir. Bu kafa karıştırıcıdır, ancak NEWID () için anahtar oluşturma ve rastgele sıralama için yararlı olması çok önemlidir.

En kafa karıştırıcı olan, deterministik olmayan işlevlerin hepsinin aslında böyle davranmamasıdır. Örneğin, RAND () ve GETDATE () sorgu başına yalnızca bir kez yürütülür.


Motorun neden / ne zaman eşittir olmayan bir menzile dönüştüğünü açıklayan bir blog yazısı veya benzeri var mı?
Bay Magoo

3
Bildiğim kadarıyla hayır. Çünkü rutin olabilir =, <ve >verimli bir B-ağaç karşı değerlendirilebilir.
David Browne - Microsoft,

5

Buna değer, bu eski SQL 92 standart belgesine bakarsanız, eşitsizlik konusundaki gereklilikler " 8.2 <comparison predicate>" bölümünde şöyle açıklanmaktadır:

1) X ve Y'ye karşılık gelen iki <satır değeri yapıcı öğe> s olsun. XV ve YV'nin sırasıyla X ve Y ile temsil edilen değerler olmasına izin verin.

[...]

ii) "X <> Y", yalnızca XV ve YV eşit değilse ve doğrudur.

[...]

7) Rx ve Ry'nin <Karşılaştırma yüklemesinin> iki <satır değeri yapıcısı> olması ve RXi ve RYi'nin sırasıyla Rx ve Ry'nin i-th <satır değeri yapıcı öğesi> olmasına izin verin. "Rx <comp op> Ry" şu şekilde doğru, yanlış veya bilinmiyor:

[...]

b) "x <> Ry", eğer sadece RXi <> RYi ise, i.

[...]

h) "x <> Ry" yanlış ise ve sadece "Rx = Ry" doğru ise.

Not: <>Karşılaştırma hakkında konuştukları için bütünlük için 7b ve 7h'yi ekledim - Satır değeri yapıcılarının birden fazla değerle karşılaştırılmasının T-SQL'de uygulandığını sanmıyorum, bunun ne söylediğini çok yanlış anlamadığım sürece - bu oldukça mümkün.

Bu bir sürü kafa karıştırıcı çöp. Ama eğer çöp bidonuna dalmaya devam etmek istiyorsan ...

Ben düşünüyorum o 1.ii biz değerlerini karşılaştırarak konum beri, bu senaryoda geçerli öğedir "satır değeri yapıcı elementler."

ii) "X <> Y", yalnızca XV ve YV eşit değilse ve doğrudur.

Temel olarak , X ve Y ile temsil edilen değerlerin eşit olmadığı X <> Ydurumlarda, doğrudur . Yana o yüklem bir mantıksal eşdeğer yeniden yazma olduğunu iyileştirici o kullanmak için, tamamen soğumasını oluyor.X < Y OR X > Y

Standart, <>karşılaştırma operatörünün her iki tarafındaki satır değeri yapıcı öğelerinin deterministikliği (veya ne pahasına olursa olsun) ile ilgili bu tanımla ilgili herhangi bir kısıtlama getirmez . Bir taraftaki değer ifadesinin deterministik olmadığı gerçeğiyle ilgilenmek kullanıcı kodunun sorumluluğundadır.


1
Oy vermekten kaçının (yukarı veya aşağı) ama ikna olmadım. Sağladığınız alıntılar "değer" dır . Anladığım kadarıyla karşılaştırma, iki taraf arasında birer ikier değerdir. Her iki taraftaki bir değerin iki (veya daha fazla) örneği arasında değil. Ayrıca, standart (en azından alıntı yaptığınız 92), deterministik olmayan tüm işlevlerde bahsetmiyor. Sizinkine benzer bir nedenden ötürü, standarda uygun bir SQL ürününün deterministik olmayan bir işlev sağlamadığını, yalnızca standartta belirtilenleri sağladığını varsayabiliriz.
ypercubeᵀᴹ

@yper Geri bildiriminiz için teşekkürler! Bence yorumunuz kesinlikle geçerli. Bu o dokümanı ilk defa okudum. Belgedeki herhangi bir yerde skaler bir alt sorgu olabileceğini söylediği "satır değeri kurucusu" tarafından temsil edilen değer bağlamındaki değerlerden bahsediyor. Özellikle skaler alt sorgu, deterministik olmayabilir gibi görünüyor. Ama neden bahsettiğimi gerçekten bilmiyorum =)
Josh Darnell
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.