JOIN deyiminde OR kullanılırken garip sorgu planı - Tablodaki her satır için sürekli tarama


10

UNIONing iki sonuç kümesi neden ORIN yan tümcesinde OR kullanarak daha iyi olabilir göstermek için bir örnek sorgu planı üretmeye çalışıyorum. Yazdığım bir sorgu planı beni çok üzdü. Users.Reputation üzerinde kümelenmemiş bir dizin ile StackOverflow veritabanı kullanıyorum.

Sorgu planı resmi Sorgu

CREATE NONCLUSTERED INDEX IX_NC_REPUTATION ON dbo.USERS(Reputation)
SELECT DISTINCT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts  
    ON Users.Id = Posts.OwnerUserId
    OR Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5

Sorgu planı https://www.brentozar.com/pastetheplan/?id=BkpZU1MZE adresinde , benim için sorgu süresi 4:37 dk, 26612 satır döndürüldü.

Daha önce var olan bir tablodan bu sürekli tarama stilinin yaratıldığını görmedim - her kullanıcı için neden sürekli bir tarama yapıldığını bilmiyorum, sürekli bir tarama genellikle kullanıcı tarafından girilen tek bir satır için kullanıldığında örneğin SELECT GETDATE (). Neden burada kullanılıyor? Gerçekten bu sorgu planı okuma bazı rehberlik takdir ediyorum.

Bunu VEYA bir BİRLİĞE böldüğümde, aynı 26612 satır döndürülmüş olarak 12 saniyede çalışan standart bir plan oluşturur.

SELECT Users.Id
FROM dbo.Users
    INNER JOIN dbo.Posts
       ON Users.Id = Posts.OwnerUserId
WHERE Users.Reputation = 5
UNION 
SELECT Users.Id
FROM dbo.Users
    INNER JOIN dbo.Posts
       ON  Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5

Bu planı şu şekilde yorumluyorum:

  • Yayınlardan 41782500 satırın tümünü alın (gerçek satır sayısı, Yayınlardaki CI taramasıyla eşleşir)
  • Yayınlardaki her 41782500 satır için:
    • Skaler üretin:
    • Expr1005: OwnerUserId
    • Expr1006: OwnerUserId
    • Expr1004: Statik değer 62
    • Expr1008: LastEditorUserId
    • Expr1009: LastEditorUserId
    • Expr1007: Statik değer 62
  • Birleştirmede:
    • Exp1010: Expr1005 (OwnerUserId) null değilse, Expr1008 (LastEditorUserID) kullanan başka bir şey kullanın
    • Expr1011: Expr1006 (OwnerUserId) boş değilse bunu kullanın, aksi takdirde Expr1009 (LastEditorUserId) kullanın
    • Expr1012: Expr1004 (62) boşsa, başka Expr1007 (62) kullanın
  • Hesaplama skalerinde: Bir ve işaretinin ne yaptığını bilmiyorum.
    • Expr1013: 4 [ve?] 62 (Expr1012) = 4 ve OwnerUserId IS NULL (NULL = Expr1010)
    • Expr1014: 4 [ve?] 62 (Expr1012)
    • Expr1015: 16 ve 62 (Expr1012)
  • Siparişe Göre Sırala:
    • Expr1013 Desc
    • Expr1014 Artan
    • Expr1010 Artan
    • Deneyim1015
  • Birleştirme Aralığı'nda Expr1013 ve Expr1015'i kaldırdı (bunlar girişlerdir, ancak çıkış değildir)
  • İçine yerleştirilmiş döngülerin altındaki Dizin aramasında, arama tahminleri olarak Expr1010 ve Expr1011 kullanıyor, ancak IX_NC_REPUTATION 'dan Expr1010 ve Expr1011 içeren alt ağaca iç içe döngü birleştirme yapmadığında bunlara nasıl erişebileceğini anlamıyorum. .
  • İç İçe Döngüler birleşimi yalnızca önceki alt ağaçta eşleşmesi olan Users.ID'lerini döndürür. Yüklemeli itme nedeniyle, IX_NC_REPUTATION üzerindeki dizin aramasından döndürülen tüm satırlar döndürülür.
  • Son İç İçe Döngüler birleştirilir: Her bir Yayın kaydı için, aşağıdaki veri kümesinde bir eşleşmenin bulunduğu Users.Id çıktısını alın.

Bir EXISTS alt sorgusunu veya alt sorgusunu denediniz mi? SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND ( EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.OwnerUserId) OR EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.LastEditorUserId) ) ;
ypercubeᵀᴹ

bir alt sorgu:SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id IN (Posts.OwnerUserId, Posts.LastEditorUserId) ) ;
ypercubeᵀᴹ

Yanıtlar:


10

Plan, burada daha ayrıntılı olarak ele aldığım plana benziyor .

PostsTablo taranır.

Her satır için OwnerUserIdve LastEditorUserId. Bu, çalışma biçimine benzer UNPIVOT. Her bir giriş satırı için iki çıkış satırı oluşturmak üzere aşağıdaki planda tek bir sabit tarama operatörü görürsünüz.

SELECT *
FROM dbo.Posts
UNPIVOT (X FOR U IN (OwnerUserId,LastEditorUserId)) Unpvt

Bu durumda plan, semantikler için biraz daha karmaşıktır, çünkü orher iki sütun değeri de aynı ise birleşimden yalnızca bir satır yayınlanmalıdır Users(iki değil)

Bunlar daha sonra birleştirme aralığından geçirilir, böylece değerler aynı olduğunda aralık daraltılır ve karşı yalnızca bir arama yürütülür Users- aksi takdirde buna karşı iki arama yürütülür .

Değer 62, arayışın eşitlik arayışı olması gerektiği anlamına gelen bir bayraktır.

ilişkin

IX_NC_REPUTATION 'dan Expr1010 ve Expr1011 içeren alt ağaç için iç içe döngü birleştirme yapmadıysanız bunlara nasıl erişimi olduğunu anlamıyorum

Bunlar sarı vurgulanan birleştirme işlecinde tanımlanır. Bu, sarı vurgulu iç içe ilmeklerin dış tarafındadır. Bu, iç içe geçmiş ilmeklerin içindeki sarı vurgulanmış aramadan önce çalışır.

resim açıklamasını buraya girin

Benzer bir plan veren bir yeniden yazma (birleştirme aralığının yerini birleştirme birliği ile değiştirilmesine rağmen), bunun yardımcı olması durumunda aşağıdadır.

SELECT DISTINCT D2.UserId
FROM   dbo.Posts p
       CROSS APPLY (SELECT Users.Id AS UserId
                    FROM   (SELECT p.OwnerUserId
                            UNION /*collapse duplicate to single row*/
                            SELECT p.LastEditorUserId) D1(UserId)
                           JOIN Users
                             ON Users.Id = D1.UserId) D2
OPTION (FORCE ORDER) 

resim açıklamasını buraya girin

PostsTabloda hangi indekslerin bulunduğuna bağlı olarak, bu sorgunun bir çeşidi önerilen UNION ALLçözümünüzden daha verimli olabilir . (veritabanımın kopyasının bunun için yararlı bir dizini yok ve önerilen çözüm iki tam tarama Postsyapıyor. Aşağıda bir taramada var)

WITH Unpivoted AS
(
SELECT UserId
FROM dbo.Posts
UNPIVOT (UserId FOR U IN (OwnerUserId,LastEditorUserId)) Unpivoted
)
SELECT DISTINCT Users.Id
FROM dbo.Users INNER HASH JOIN Unpivoted
       ON  Users.Id = Unpivoted.UserId
WHERE Users.Reputation = 5

resim açıklamasını buraya girin

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.