Filtrelenmiş dizin yalnızca filtrelenen kısım NEREDE değil JOIN'deyken kullanılır


10

Aşağıda filtrelenmiş dizini oluşturdum ancak 2 sorguyu daha aşağı çalıştırdığımda, bu dizin yalnızca END_DTTM'in JOIN'de nerede yan tümcesinden ziyade ilk örnekte bir arama için kullanılır (bu, sorgulardaki tek farktır) . Bunun neden olduğunu kimse açıklayabilir mi?

Endeks Oluşturma

CREATE NONCLUSTERED INDEX [ix_PATIENT_LIST_BESPOKE_LIST_ID_includes] ON [dbo].[PATIENT_LIST_BESPOKE] 
(
    [LIST_ID] ASC,
    [END_DTTM] ASC
)
WHERE ([END_DTTM] IS NULL)

Sorguları

DECLARE @LIST_ID INT = 3655

--This one seeks on the index

SELECT  
    PATIENT_LISTS.LIST_ID
FROM    
    DBO.PATIENT_LISTS
    LEFT JOIN DBO.PATIENT_LIST_BESPOKE ON PATIENT_LISTS.LIST_ID = PATIENT_LIST_BESPOKE.LIST_ID  
                                      AND PATIENT_LIST_BESPOKE.END_DTTM IS NULL
WHERE
    PATIENT_LISTS.LIST_ID = @LIST_ID

--This one scans on the index

SELECT  
    PATIENT_LISTS.LIST_ID
FROM    
    DBO.PATIENT_LISTS
    LEFT JOIN DBO.PATIENT_LIST_BESPOKE ON PATIENT_LISTS.LIST_ID = PATIENT_LIST_BESPOKE.LIST_ID  
WHERE   
    PATIENT_LISTS.LIST_ID = @LIST_ID AND
    PATIENT_LIST_BESPOKE.END_DTTM IS NULL   

Yanıtlar:


12

Optimize edicinin yüklemi bir diziyle eşleştirmesi için (filtrelenmiş veya başka türlü), yüklemin mantıksal sorgu ağacında Al işleminin yanında görünmesi gerekir. Bunu kolaylaştırmak için, tahminler genellikle optimizasyon başlamadan önce mantıksal ağacın yapraklarına mümkün olduğunca yakın itilir.

Büyük ölçüde basitleştirmek için, fiziksel dizin stratejisi uygulaması bunu yapar:

Predicate + Logical Get -> Physical Get (using Index)

İlgilendiğiniz sorgu, dış birleşimin üzerindeki yüklemle başlar:

Predicate on T2 --+-- LOJ -- Get (T1)
                       |
                       +---- Get (T2)

Bu biçim, dizin Getir'e bitişik olmadığından dizin stratejisi kuralıyla eşleşmiyor. Bu nedenle, cevabın ilk kısmı, yüklem dış birleşimin ötesine itilmediği sürece filtrelenmiş dizin eşleşmesinin başarısız olacağıdır.

İkinci bölüm, optimizasyonun korunmuş taraftaki bir dış birleşimin ötesine geçmesi için gerekli keşif kuralını içermemesidir, çünkü dönüşüm çok nadiren geçerlidir. Optimize edicinin genel bir özelliği, yalnızca en sık kullanılan kuralların uygulanmasıdır.

Sonuç olarak, filtrelenmiş dizinin eşleştirilmesi bu durumda başarısız olur. Açıkça belirtmek gerekirse , yeniden yazma, alıntıladığınız çok özel durumda (ikinci sorgu) geçerlidir.

İlk sorgu formu için (farklı semantiklerle), yüklem başlangıçtan birleştirme ile ilişkilidir ve yüklem aşağı itme mantığı bunu Get'e kısa bir mesafeye taşıyabilir, çünkü dış birleştirmeyi geçmesi gerekmez Yukarıda açıklanmıştır.

Arka plan ve daha fazla bilgi:


9

Bunlar değil sonra diğer kutu filtre katılmadan önce bir filtre beri, anlamsal olarak aynı sorguları. Daha basit bir örnekle açıklayayım:

CREATE TABLE dbo.Lefty(LeftyID INT PRIMARY KEY);

CREATE TABLE dbo.Righty(LeftyID INT, SomeList INT);

INSERT dbo.Lefty(LeftyID) VALUES(1),(2),(3);

INSERT dbo.Righty(LeftyID, SomeList) VALUES(1,1),(1,NULL),(2,2);

Sorgu 1 her üç satırı da döndürür:

SELECT l.LeftyID, r.SomeList
FROM dbo.Lefty AS l
LEFT OUTER JOIN dbo.Righty AS r
ON l.LeftyID = r.LeftyID
AND r.SomeList IS NULL;

Ancak sorgu 2, LeftyID 2'yi bırakır:

SELECT l.LeftyID, r.SomeList
FROM dbo.Lefty AS l
LEFT OUTER JOIN dbo.Righty AS r
ON l.LeftyID = r.LeftyID
WHERE r.SomeList IS NULL;

SQLfiddle kanıtı

Anti-semi birleştirme gerçekleştirmeye çalışıyorsanız, test edilen sütunun boş bırakılamaz olması gerekir . ON ve WHERE arasındaki ölçütleri taşımak, yalnızca INNER birleşimleriyle uğraşırken mantıklı bir fark oluşturmaz, ancak OUTER ile önemli bir fark vardır. Ayrıca, sonuçlarınızın doğru bir şekilde filtrelenmiş bir dizinin kullanıp kullanamayacağından daha fazla önem vermeniz gerekir.


cevap için teşekkürler ama sorguları aynı olduğunu iddia etmiyorum, neden bir sorgu filtrelenmiş dizini kullanır ve diğer değil soruyorum.
chris

@chris Bu dizini bir dizin ipucu ile zorlamaya çalıştınız mı? Gerçek, infaz sonrası planları bu ipucu ile ve ipucu olmadan karşılaştırmayı merak ediyorum. Bana göre optimizer, anti-semi birleştirme yaptığına inandığında bu dizini görmezden geliyor (çünkü bu durumda boş değerli bir sütunun kullanılmasını beklemeyeceği için), ancak bunun maliyetlendirme veya işlem sırası veya sol taraftan gelen filtrelenmiş dizindekilerden çok daha fazla satır olabileceğinin altında yatan bazı bilgilerle ilgilidir. Planları görmek yardımcı olabilir.
Aaron Bertrand

3

İki sorgu farklıdır - anlam ve sonuç olarak. İşte bir yeniden yazma, bu yüzden iki sorgunun ne yaptığı daha açık:

-- 1st query
SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
    LEFT JOIN  
      ( SELECT LIST_ID                    -- the filtered index
        FROM   DBO.PATIENT_LIST_BESPOKE   -- can be used
        WHERE  END_DTTM IS NULL           -- for the subquery
      ) AS b
    ON  a.LIST_ID = b.LIST_ID ;           -- and the join

ve 2.:

-- 2nd query
SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
    JOIN  
      ( SELECT LIST_ID                    -- the filtered index
        FROM   DBO.PATIENT_LIST_BESPOKE   -- can be used
        WHERE  END_DTTM IS NULL           -- for the subquery
      ) AS b
    ON  a.LIST_ID = b.LIST_ID             -- and the join

UNION ALL

SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
WHERE NOT EXISTS  
      ( SELECT *
        FROM   DBO.PATIENT_LIST_BESPOKE AS b
        WHERE  a.LIST_ID = b.LIST_ID         -- but not for this
      ) ;

Şimdi 2nq sorgusunun 2. kısmı için, filtrelenmiş dizin kullanılamaz olduğunu oldukça açık düşünüyorum.


Ayrıntılı olarak, bu sorgularla ilgili LIST_IDolarak, ilk tabloda 4 tür değer vardır:

  • (a) tümü ile birlikte ikinci tabloda eşleşen satırlara sahip değerler END_DTTM IS NULL.

  • her ikisi de, ikinci tabloda satır birbirine uyan (b) değerleri END_DTTM IS NULLile END_DTTM IS NOT NULL.

  • (c) ikinci tabloda eşleşen satırlara sahip değerlerin tümü END_DTTM IS NOT NULL.

  • (d) ikinci tabloda eşleşen satır bulunmayan değerler.

Şimdi, ilk sorgu (a) ve (b) türündeki tüm değerleri muhtemelen birçok kez (ikinci tabloda eşleşen bir satıra sahip oldukları kadar END_DTTM IS NULL) ve (c) ve (d) türündeki tüm satırları tam olarak bir kez ( dış birleştirmenin eşleşmeyen kısmıdır).

2. sorgu, (a) ve (b) türünün tüm değerlerini muhtemelen birçok kez döndürür (ikinci tabloda eşleşen bir satıra sahip oldukları kadar END_DTTM IS NULL) ve (d) türündeki tüm satırları tam olarak bir kez döndürür.
Bu olacak olmayan ikinci tabloda eşleşen satırları bulacaksınız birleştirme, çünkü, (c) 'nin herhangi bir değer iade (ancak bu olacaktır END_DTTM IS NOT NULL) ve daha sonra bunlar ile kaldırılır WHEREmadde.

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.