Özyinelemeli ortak tablo ifadesinde EXCEPT kullanma


33

Aşağıdaki sorgu neden sonsuz satır döndürüyor? Ben umuyordum EXCEPTÖzyinelemeyi sonlandırmak için maddesini ..

with cte as (
    select *
    from (
        values(1),(2),(3),(4),(5)
    ) v (a)
)
,r as (
    select a
    from cte
    where a in (1,2,3)
    union all
    select a
    from (
        select a
        from cte
        except
        select a
        from r
    ) x
)
select a
from r

Yığın Taşması ile ilgili bir soruyu cevaplamaya çalışırken rastladım .

Yanıtlar:


26

Özyinelemeli bir CTE'deki mevcut durum hakkında bilgi için Martin Smith'in cevabına bakınız EXCEPT.

Ne gördüğünü ve nedenini açıklamak için:

Burada, değişken değerleri ile özyinelemeli öğe arasındaki farkı daha net hale getirmek için bir tablo değişkeni kullanıyorum (anlambilim değiştirmez).

DECLARE @V TABLE (a INTEGER NOT NULL)
INSERT  @V (a) VALUES (1),(2)
;
WITH rCTE AS 
(
    -- Anchor
    SELECT
        v.a
    FROM @V AS v

    UNION ALL

    -- Recursive
    SELECT
        x.a
    FROM
    (
        SELECT
            v2.a
        FROM @V AS v2

        EXCEPT

        SELECT
            r.a
        FROM rCTE AS r
    ) AS x
)
SELECT
    r2.a
FROM rCTE AS r2
OPTION (MAXRECURSION 0)

Sorgu planı:

Özyinelemeli CTE Planı

Yürütme, planın kökünden başlar (SEÇİM) ve kontrol, ağaçtan Dizin Biriktirme, Birleştirme ve sonra en üst düzey Tablo Taramasına geçer.

Taramadaki ilk satır ağacı keser ve (a) Yığın Makarasında depolanır ve (b) müşteriye geri döner. İlk önce hangi satır tanımlanmadı, ama argüman uğruna, {1} değerine sahip satır olduğunu varsayalım. Görünecek ilk satır bu nedenle {1}.

Kontrol tekrar Tablo Taraması'na geçer (Birleştirme operatörü, bir sonrakini açmadan önce en üstteki girişten tüm satırları tüketir). Tarama, ikinci satırı (değer {2}) yayar ve bu da yine istifte depolanacak ve istemciye çıkacak ağacı geçer. Müşteri şimdi {1}, {2} dizisini aldı.

LIFO yığınının solda olduğu bir kural benimsendiğinde, yığın şimdi {2, 1} içeriyor. Kontrol tekrar Tablo Taraması'na geçtiğinde, artık satır bildirmez ve kontrol, ikinci girişini açan (yığma makarasına geçmek için bir sıraya ihtiyaç duyar) ve birleştirme işlemine geri döner. ilk kez.

İç birleştirme dış girişinde {2} yığından üst satırı okuyan ve çalışma masasından silen Tablo Biriktirmesini çağırır. Yığın şimdi {1} içeriyor.

Dış girişinde bir satır alan İç Birleştirme, iç girişini sol Anti-Yarı Birleştirme'ye (LASJ) doğru kontrol eder. Bu, dış girişten bir satır isteyerek kontrolü Sort'a geçirir. Sıralama engelleyen bir yineleyicidir, bu nedenle tablo değişkenindeki tüm satırları okur ve bunları (olduğu gibi) artan şekilde sıralar.

Sıralama tarafından yayılan ilk satır bu nedenle {1} değeridir. LASJ'nin iç tarafı özyinelemeli öğenin geçerli değerini (yığından yeni çıkmış olan) (2}) döndürür. LASJ'deki değerler {1} ve {2} 'dir, bu nedenle değerler eşleşmediğinden {1} verilir.

Bu satır {1}, sorgu planı ağacını şimdi {1, 1} içeren ve istemciye yayılan yığına eklendiği Dizin (Yığın) Biriktiricisine akıtır. Müşteri şimdi {1}, {2}, {1} dizisini aldı.

Kontrol şimdi birleştirme işlemine geri döner, iç tarafa geri döner (son kez bir satır döndürür, tekrar yapabilir), İç Birleşimden aşağıya, LASJ'ye. Sıralamadan {2} değerini alarak iç girdisini tekrar okur.

Özyinelemeli üye hala {2}, yani bu sefer LASJ {2} ve {2} 'yi buluyor ve sonuçta hiçbir satır yayılmıyor. İç girişinde artık sıra bulunmaz (Sıralama şimdi sıraların dışındadır), kontrol İç Bağlantıya geri döner.

İç Birleştirme dış girişini okur, bu da {1} değerinin {1, 1} yığınından çıkarılması ve yığını sadece {1} ile bırakmasıyla sonuçlanır. Şimdi işlem, LASJ testini geçen ve yığına eklenmekte olan Tablo Tarama ve Sıralama'nın yeni bir çağrısından gelen {2} değeri ile tekrar eder ve şimdi {1}, {2} alan müşteriye geçer. {1}, {2} ... ve yuvarlanıyoruz.

Özyinelemeli CTE planlarında kullanılan Stack makarasının en sevdiğim açıklaması Craig Freedman's.


31

Özyinelemeli CTE'lerin BOL açıklaması özyinelemeli yürütmenin açıklar:

  1. CTE ifadesini bağlantı ve özyinelemeli üyelere ayırın.
  2. İlk aşılamayı veya temel sonuç kümesini (T0) oluşturan bağlantı üyelerini çalıştırın.
  3. Özyinelemeli üye (ler) i giriş olarak Ti ile ve giriş olarak Ti + 1 ile çalıştırın.
  4. Boş bir set geri dönene kadar 3. adımı tekrarlayın.
  5. Sonuç kümesini döndür. Bu, T0'dan Tn'ye kadar BÜTÜN BİR BİRLİKTİR.

Yukarıdakilerin mantıklı bir açıklaması olduğunu unutmayın. İşlemlerin fiziksel sırası burada gösterildiği gibi biraz farklı olabilir

Bunu CTE'nize uygulamak, aşağıdaki düzende sonsuz bir döngü beklerdim

+-----------+---------+---+---+---+
| Invocation| Results             |
+-----------+---------+---+---+---+
|         1 |       1 | 2 | 3 |   |
|         2 |       4 | 5 |   |   |
|         3 |       1 | 2 | 3 |   |
|         4 |       4 | 5 |   |   |
|         5 |       1 | 2 | 3 |   |
+-----------+---------+---+---+---+ 

Çünkü

select a
from cte
where a in (1,2,3)

Çapa ifadesidir. Bu açıkça döner 1,2,3olarakT0

Bundan sonra özyinelemeli ifade çalışır

select a
from cte
except
select a
from r

İle 1,2,3bir çıkış verecektir girdi olarak 4,5olarak T1o zaman dönecektir özyinelemeye sonraki turda içinde Sözünü geri takmayı 1,2,3vb süresiz ve.

Ancak gerçekte olan bu değil. Bunlar ilk 5 çağrının sonucudur

+-----------+---------+---+---+---+
| Invocation| Results             |
+-----------+---------+---+---+---+
|         1 |       1 | 2 | 3 |   |
|         2 |       1 | 2 | 4 | 5 |
|         3 |       1 | 2 | 3 | 4 |
|         4 |       1 | 2 | 3 | 5 |
|         5 |       1 | 2 | 3 | 4 |
+-----------+---------+---+---+---+

Artışlarla kullanım OPTION (MAXRECURSION 1)ve yukarı doğru ayarlamadan, 1art arda her seviyenin sürekli olarak çıkış 1,2,3,4ve arasında geçiş yapacağı bir döngüye girdiği görülebilir 1,2,3,5.

Tarafından açıklandığı gibi @Quassnoi içinde bu blog yayınında . Gözlemlenen sonuçların örneği, her çağrı önceki işlemden son satırın (1),(2),(3),(4),(5) EXCEPT (X)olduğu yerdedir X.

Düzenleme: SQL Kiwi'nin mükemmel cevabını okuduktan sonra , bunun neden ortaya çıktığı ve bunun hala işlenemeyen bir sürü şey olduğu konusundaki tüm hikayenin olmadığı açıktır.

Çapa 1,2,3istemciye gönderir3,2,1

3 yığılmış yığılmış, yığın içeriği 2,1

LASJ 1,2,4,5, Yığın İçeriği döndürür5,4,2,1,2,1

5 yığılmış yığılmış, yığın içeriği 4,2,1,2,1

LASJ 1,2,3,4 Yığın İçeriğini döndürür4,3,2,1,5,4,2,1,2,1

4 yığılmış yığılmış, yığın içeriği 3,2,1,5,4,2,1,2,1

LASJ 1,2,3,5 Yığın İçeriğini döndürür5,3,2,1,3,2,1,5,4,2,1,2,1

5 yığılmış yığılmış, yığın içeriği 3,2,1,3,2,1,5,4,2,1,2,1

LASJ 1,2,3,4 Yığın İçeriğini döndürür4,3,2,1,3,2,1,3,2,1,5,4,2,1,2,1

Özyinelemeli üyeyi mantıksal olarak eşdeğer (yinelenen / NULL'ların yokluğunda) ifadesiyle değiştirmeye çalışırsanız

select a
from (
    select a
    from cte
    where a not in 
    (select a
    from r)
) x

Buna izin verilmiyor ve "Alt sorgularda özyineleli başvurulara izin verilmiyor" hatasını veriyor. Bu yüzden belki de EXCEPTbu durumda bile izin verilen bir gözetimdir .

Ekleme: Microsoft şimdi Connect Feedback’e aşağıdaki gibi yanıt verdi :

Jack'in tahmini doğru: bu bir sözdizimi hatası olmalıydı; özyinelemeli referanslara gerçekten de EXCEPTmaddelerde izin verilmemelidir . Bu hatayı yaklaşan bir servis sürümünde ele almayı planlıyoruz. Bu arada, özyinelemeli referansları önlemek için öneriyorumEXCEPT cümlelerde .

Yinelemeyi kısıtlamada, yinelemenin EXCEPTgetirilmesinden bu yana bu kısıtlamayı içeren ANSI SQL standardını takip ediyoruz (1999'da inanıyorum). EXCEPTSQL gibi bildirici dillerde , anlambilimde yinelemenin ne olması gerektiği konusunda ("kasıtsız olumsuzlama" olarak da bilinir) yaygın bir anlaşma yoktur . Ek olarak, bir RDBMS sisteminde bu anlambilimin verimli bir şekilde (makul büyüklükteki veri tabanları için) uygulanması oldukça zordur (mümkün değilse).

Ve nihayetinde uygulama 2014 yılında 120 ve daha üst düzeyde uyumluluk seviyesine sahip veritabanları için yapılmış gibi görünüyor .

Bir EXCEPT yan tümcesinde özyinelemeli referanslar, ANSI SQL standardına uygun bir hata oluşturur.

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.