EĞER VARLIKLAR gömülü select deyiminden daha uzun sürerse


35

Aşağıdaki kodu çalıştırdığımda 22.5 dakika sürüyor ve 106 milyon okuyor. Ancak, sadece inner select deyimini kendim çalıştırırsam, sadece 15 saniye sürer ve 264k okur. Yan not olarak, seçim sorgusu kayıt döndürmez.

Bunun neden IF EXISTSbu kadar uzun sürdüğünü ve bu kadar çok okuma yaptığını bilen bir fikrin var mı? Ayrıca select deyimini yapmak için değiştirdim ve SELECT TOP 1 [dlc].[id]2 dakika sonra öldürdüm.

Geçici bir düzeltme olarak, bir sayı (*) yapmak ve bu değeri bir değişkene atamak için değiştirdim @cnt. Sonra bir IF 0 <> @cntaçıklama yapar . Ancak EXISTSdaha iyi olacağını düşündüm , çünkü select deyiminde döndürülen kayıtlar olsaydı, en az bir kayıt bulduktan sonra tarama yapmayı / aramayı durduracak, oysa count(*)tam sorguyu tamamlayacaktı. Neyi kaçırıyorum?

IF EXISTS
   (SELECT [dlc].[ID]
   FROM TableDLC [dlc]
   JOIN TableD [d]
   ON [d].[ID] = [dlc].[ID]
   JOIN TableC [c]
   ON [c].[ID] = [d].[ID2]
   WHERE [c].[Name] <> [dlc].[Name])
BEGIN
   <do something>
END

4
Satır hedefi probleminden kaçınmak için başka bir fikir (denenmemiş, dikkat edin!) Tersini denemek olabilir IF NOT EXISTS (...) BEGIN END ELSE BEGIN <do something> END.
Aaron Bertrand

Yanıtlar:


32

Bunun neden IF EXISTSbu kadar uzun sürdüğünü ve bu kadar çok okuma yaptığını bilen bir fikrin var mı? Ayrıca select deyimini yapmak için değiştirdim ve SELECT TOP 1 [dlc].[id]2 dakika sonra öldürdüm.

Bu soruya cevabımda açıkladığım gibi:

TOP bir uygulama planını nasıl (ve neden) etkiler?

Kullanımı EXISTS, en iyi duruma getiricinin ilk sırayı hızlıca bulmayı amaçlayan bir yürütme planı hazırladığı bir satır hedefi sunar. Bunu yaparken, verilerin eşit şekilde dağıldığını varsaymaktadır. Örneğin, istatistikler 100.000 satırda beklenen 100 eşleşme olduğunu gösteriyorsa, ilk eşleşmeyi bulmak için yalnızca 1000 satır okumak zorunda kalacağını varsayacaktır.

Bu varsayımın hatalı olduğu ortaya çıkarsa, beklenenden daha uzun yürütme süresine neden olur. Örneğin, SQL Server aramada çok geçe ilk eşleme değerini bulmak için gerçekleşen bir erişim yöntemi (örn. Sırasız tarama) seçerse, neredeyse tamamen taramayla sonuçlanabilir. Öte yandan, ilk birkaç satır arasında eşleşen bir satır bulunursa, performans çok iyi olacaktır. Bu, sıralı hedeflerle temel risk - tutarsız performans.

Geçici bir düzeltme olarak, bir sayı (*) yapmak ve bu değeri bir değişkene atamak için değiştirdim.

Sorguyu, satır hedefi atanmayacak şekilde yeniden düzenlemek genellikle mümkündür. Satır hedefi olmadan, ilk eşleşen satırla karşılaşıldığında (doğru yazılırsa) sorgu hala sona erebilir, ancak yürütme planı stratejisinin farklı olması (ve umarım daha etkili) olması olasıdır. Açıkçası, count (*) tüm satırları okumayı gerektirir, bu yüzden mükemmel bir alternatif değildir.

SQL Server 2008 R2 veya sonraki bir sürümünü kullanıyorsanız, genel olarak belgelenmemiş ve desteklenmeyen izleme bayrağı 4138'i , bir satır hedefi olmadan bir yürütme planı almak için de kullanabilirsiniz . Bu bayrak, desteklenen bir ipucu kullanılarak da belirtilebilir OPTION (QUERYTRACEON 4138), ancak bir plan kılavuzunda kullanılmadıkça çalışma zamanı sysadmin izni gerektirdiğini unutmayın .

ne yazık ki

Yukarıdakilerin hiçbiri IF EXISTSkoşullu bir ifade ile işlevsel değildir . Sadece normal DML için geçerlidir. O edecek alternatif çalışmak SELECT TOP (1)denedin formülasyon. Bu, daha COUNT(*)önce belirtildiği gibi tüm nitelikli satırları sayması gereken kullanmaktan daha iyi olabilir .

Bununla birlikte, aramayı erken sona erdirirken, satır hedefinden kaçınmanıza veya kontrol etmenize olanak sağlayacak olan bu gereksinimi ifade etmenin birçok yolu vardır. Son bir örnek:

DECLARE @Exists bit;

SELECT @Exists =
    CASE
        WHEN EXISTS
        (
            SELECT [dlc].[ID]
            FROM TableDLC [dlc]
            JOIN TableD [d]
            ON [d].[ID] = [dlc].[ID]
            JOIN TableC [c]
            ON [c].[ID] = [d].[ID2]
            WHERE [c].[Name] <> [dlc].[Name]
        )
        THEN CONVERT(bit, 1)
        ELSE CONVERT(bit, 0)
    END
OPTION (QUERYTRACEON 4138);

IF @Exists = 1
BEGIN
    ...
END;

Sağladığınız alt örnek 3,75 dakika içinde çalıştı ve 46 m okuma yaptı. Bu yüzden, orijinal sorgundan daha hızlı olsa da, bu durumda cnt = count (*) ile devam edeceğim ve değişkeni sonradan değerlendireceğim. Özellikle bu sürenin% 99'undan beri içinde hiçbir şey kalmayacak. Sizin ve Rob’un verdiği yanıtlara dayanarak, bir tür sonuç beklemeniz gerçekten iyi bir sonuçtur ve bu sonuç verilerinize eşit olarak dağıtılmış gibi görünür.
Chris Woods,

3
@ChrisWoods: "Özellikle bu koşuların% 99'undan beri içinde hiçbir şey olmayacak" dedin. Bu hemen hemen hiç birinin satır hedefinin kötü bir fikir olduğunu garanti eder, çünkü orada genellikle HAYIR satır olmamasını ve hiç olmadığını görmek için her şeyi taramak zorunda kalmanızı bekler. Akıllıca bir dizin ekleyemiyorsanız, COUNT (*) işaretini kullanın.
Ross Presser,

25

EXISTS'in yalnızca bir satır bulması gerektiğinden, bir satır hedefi kullanacaktır. Bu bazen ideal olmayan bir plan üretebilir. Bunun sizin için böyle olacağını umuyorsanız, değişkeni a'nın sonucuyla COUNT(*)doldurun ve sonra değişkeni 0'dan fazla olup olmadığını görmek için sınayın.

Yani ... Küçük bir satır hedefi ile karma tablolar oluşturmak veya birleştirme birleşmeleri için yararlı olabilecek akışları ayırmak gibi işlemleri engellemekten kaçınacaktır, çünkü oldukça hızlı bir şeyler bulmak zorunda kalacağını ve bu nedenle yuvalanmış döngüler olacağını Bir şey bulmuşsa en iyisi ol. Bunun dışında tüm set boyunca çok daha kötü bir plan yapabilir. Tek bir satır bulmak hızlı olsaydı, bloklardan kaçınmak için bu yöntemi kullanmak isterdiniz ...

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.