Satır içi görünüm ve WITH deyimi arasındaki fark nedir?


9

Satır içi görünümler, bir alt sorgudan farklı bir tablo gibi seçim yapmanıza olanak tanır:

SELECT
    *
FROM /* Selecting from a query instead of table */
    (
        SELECT
            c1
        FROM
            t1
        WHERE
            c1 > 0
    ) a
WHERE
    a.c1 < 50;

Bunu farklı terimler kullanarak ifade gördüm: satır içi görünümler, yan tümce, CTE ve türetilmiş tablolar. Bana göre onlar aynı şey için farklı satıcıya özel sözdizimi gibi görünüyor.

Bu yanlış bir varsayım mı? Bunlar arasında herhangi bir teknik / performans farkı var mı?


5
Standart SQL'den "resmi" isimler Türetilmiş Tablo (Oracle'ın Satır İçi Görünümü olarak adlandırdığı ) ve Ortak Tablo İfadesidir (= WITH...). Her Türetilmiş Tabloyu bir CTE olarak yeniden yazabilirsiniz, ancak tam
tersi olamaz

Yanıtlar:


8

Oracle'da satır içi görünümler (türetilmiş tablolar) ve WITH yan tümcesi (CTE) arasında bazı önemli farklılıklar vardır. Bazıları oldukça evrenseldir, yani diğer RDBMS için geçerlidir.

  1. WITH özyinelemeli alt sorgular oluşturmak için kullanılabilir, satır içi görünüm -not (aynı bildiğim kadarıyla CTE'yi destekleyen tüm RDBMS için)
  2. Fıkradaki alt sorgunun WITHöncelikle fiziksel olarak yürütülmesi daha olasıdır; birçok durumda, WITHve satır içi görünümü arasında seçim yapmak farklı yürütme planları seçmek için optimize edici yapar (sanırım satıcıya özgü, hatta versiyona özgü).
  3. Alt sorgu WITHgeçici bir tablo olarak gerçekleştirilebilir (başka bir satıcı olup olmadığını bilmiyorum ama Oracle bu özelliği destekliyor).
  4. İçinde alt WITHsorgu birden çok kez, diğer alt sorgularda ve ana sorguda (çoğu RDBMS için geçerlidir) başvurulabilir.

MySQL (en azından en son MariaDB sürümleri) türetilmiş tabloları gerçekleştirebilir (hatta dizin ekleyebilir).
ypercubeᵀᴹ

3
Bir yan fayda olarak, CTE'lerin kullanılmasının genellikle insanlar için de daha okunabilir olduğunu eklemek isterim.
Joishi Bodio

@JoishiBodio: Şahsen sana katılıyorum, ancak okunabilirlik oldukça öznel bir mesele. Bahsetmekten kaçınmak istiyorum
a1ex07

Ek olarak, bir CTE daha önce beyan edilmiş bir CTE'ye başvurabilir. Türetilmiş bir tablo, kullanılmadıkça önceden bildirilmiş bir türetilmiş tabloya aynı düzeyde başvuru yapamaz LATERAL.
Lennart

8

Diğer cevaplar sözdizimi farklılıklarını oldukça iyi kapsıyor, bu yüzden buna girmeyeceğim. Bunun yerine bu cevap sadece Oracle'daki performansı kapsayacaktır.

Oracle optimizer, bir CTE'nin sonuçlarını dahili bir geçici tablo haline getirmeyi seçebilir. Maliyet tabanlı optimizasyon yerine bunu yapmak için bir buluşsal yöntem kullanır. Buluşsal yöntem "Önemsiz bir ifade değilse ve CTE'ye sorguda birden çok kez başvuruluyorsa CTE'yi materyalize edin" gibi bir şeydir. Gerçekleştirmenin performansı artıracağı bazı sorgular vardır. Gerçekleştirmenin performansı önemli ölçüde düşüreceği bazı sorgular vardır. Aşağıdaki örnek biraz anlaşılmıştır, ancak noktayı iyi göstermektedir:

İlk olarak, 1'den 10000'e kadar tamsayılar içeren birincil anahtarlı bir tablo oluşturun:

CREATE TABLE N_10000 (NUM_ID INTEGER NOT NULL, PRIMARY KEY (NUM_ID));

INSERT /*+APPEND */ INTO N_10000
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 10000
ORDER BY LEVEL;

COMMIT;

Türetilmiş iki tablo kullanan aşağıdaki sorguyu düşünün:

SELECT t1.NUM_ID
FROM 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t1
LEFT OUTER JOIN 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Bu sorguya bakabilir ve herhangi bir satır döndürmeyeceğini hızlı bir şekilde belirleyebiliriz. Oracle, bunu belirlemek için dizini de kullanabilmelidir. Makinemde sorgu aşağıdaki planla neredeyse anında bitiyor:

iyi plan

Kendimi tekrarlamayı sevmiyorum, bu yüzden aynı sorguyu bir CTE ile deneyelim:

WITH N_10000_CTE AS (
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

İşte plan:

kötü plan

Bu gerçekten kötü bir plan. Dizin kullanmak yerine Oracle, 10000 X 10000 = 100000000 satırı geçici bir tablo halinde yalnızca sonunda 0 satır döndürecek şekilde düzenler. Bu planın maliyeti diğer sorgudan çok daha yüksek olan 6 M civarındadır. Sorgunun makinemde tamamlanması 68 saniye sürdü.

Geçici tablo alanında yeterli bellek veya boş alan yoksa, sorgunun başarısız olabileceğini unutmayın.

Doktorun INLINECTE'yi gerçekleştirmesine izin vermemek için belgesiz ipucunu kullanabilirim :

WITH N_10000_CTE AS (
  SELECT /*+ INLINE */ n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Bu sorgu dizini kullanabilir ve neredeyse anında biter. Sorgunun maliyeti önceki 11 ile aynıdır. Dolayısıyla, ikinci sorgu için Oracle tarafından kullanılan buluşsal yöntem, tahmin edilen maliyeti 11 olan bir sorgu yerine tahmini maliyeti 6 M olan bir sorgu seçmesine neden oldu.


1

SQL Server için, WITH CTEgeçici adlandırılmış sonuç kümesini belirtir, ancak yalnızca ilki için gereklidir CTE. yani

WITH CTE AS (SELECT .... FROM), 
CTE2 AS (SELECT .... FROM)

SELECT CTE.Column, CTE2.Column
FROM CTE
INNER JOIN CTE2 on CTE.Column = CTE2.Column

Ancak bu bir alt sorgu veya ilişkili alt sorgu değildir. CTE'de başvurulan Tabloları güncellemek gibi bir SQL Server'daki alt sorgu ile yapamayacağınız bir CTE ile yapabileceğiniz şeyler vardır. Tabloyu CTE ile güncelleme örneği .

Bir alt sorgu şöyle bir şey olurdu

SELECT
   C1,
   (SELECT C2 FROM SomeTable) as C2
FROM Table

Veya a.c1 sonuçlarına göre sonuçlarınıza başvuruda bulunacak / birleştirilecek / sınırlandıracaksanız, OP'nizde sağladığınız ilişkili bir alt sorgu.

Yani, kesinlikle aynı şey değildir, ancak birçok durumda aynı sonucu elde etmek için bu yöntemlerden birini veya daha fazlasını kullanabilirsiniz. Bu sadece nihai sonucun ne olduğuna bağlı.


1

withOracle'daki yan tümce ve alt sorgu arasındaki temel fark, yan tümce içindeki bir sorguya birden çok kez başvurabilmenizdir. Daha sonra materializeipucu kullanarak bir geçici tabloya dönüştürmek gibi bazı optimizasyonlar yapabilirsiniz . Ayrıca, bir withcümle içinde kendisini göstererek yinelemeli sorgular da yapabilirsiniz . Bunu satır içi görünümle yapamazsınız.

Daha fazla bilgiyi burada ve burada bulabilirsiniz .


Genel olarak somutlaştırma ipucu gerekli değildir. Oracle optimizer, varsayılan olarak CTE'nin gerçekleştirilmesinin anlamlı olup olmadığına karar verir - ancak optimizasyon değerlendirmesinin ipucunu üzerine yazabilirsiniz MATERIALIZE. INLINEtam tersi için.
Wernfried Domscheit

@WernfriedDomscheit bu doğru. Ancak bazen optimizer CTE'yi gerçekleştirmeyi seçmez ve bu durumda materializeipucu kullanmak geçerli bir seçenektir. CTE'yi gerçekleştirmenin yürütme planına fayda sağlayacağını bildiğim çok karmaşık sorguları optimize ederken bazen belirtmem gerekiyordu.
Marko Vodopija

0

SQL sunucusundaki CTE'lere dikkat etmeniz gerekir, sadece oracle değil, CTE'leri kullanırken sorguların alt sorgular, çapraz uygulama vb. İle karşılaştırıldığında çok daha kötü performans gösterdiği durumlar vardır.

Her zaman olduğu gibi, hangisinin en iyi sonucu verdiğini belirlemek için herhangi bir sorguyu çeşitli yük koşullarında test etmek önemlidir.

@Scsimon ile aynı şekilde, bazen MS SQL sunucusu dizin kullanımı ile ilgili beklediğinizi yapmaz.

Aynı verileri bir kereden fazla kullanacaksanız, CTE'ler daha yararlı olabilir, yalnızca bir kez kullanıyorsanız, büyük veri kümelerinde genellikle bir alt sorgu daha hızlıdır.

örneğin seçin (alt sorgu) başka bir şeye katılmak ...

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.