Ortak Tablo İfadesi (CTE) ne zaman kullanılır?


230

Ortak Tablo İfadesi hakkında okumaya başladım ve bunları kullanmam gereken bir kullanım durumu düşünemiyorum. Türetilmiş tablolarda da yapılabileceği için gereksiz gibi görünmektedir. Eksik ya da iyi anlamadığım bir şey var mı? Birisi bana CTE vakasını yapmak için düzenli seçim, türetilmiş veya geçici tablo sorguları ile basit bir sınırlama örneği verebilir mi? Herhangi bir basit örnek çok takdir edilecektir.

Yanıtlar:


197

Bir örnek, aynı veri kümesine birden çok kez başvurmanız / katılmanız gerekiyorsa bunu bir CTE tanımlayarak yapabilirsiniz. Bu nedenle, bir kod yeniden kullanım biçimi olabilir.

Öz referanslama örneği özyinelemedir: CTE Kullanan Özyinelemeli Sorgular

Çevrimiçi Kitaplardan Alınan Heyecan Verici Microsoft Tanımları İçin :

Bir CTE aşağıdakiler için kullanılabilir:

  • Özyinelemeli bir sorgu oluşturun. Daha fazla bilgi için, bkz . Ortak Tablo İfadelerini Kullanan Yinelenen Sorgular.

  • Bir görünümün genel kullanımı gerekli olmadığında bir görünümün yerine kullanılır; yani, tanımı meta verilerde saklamanız gerekmez.

  • Skaler alt seçimden türetilen bir sütuna veya deterministik olmayan veya harici erişime sahip bir işleve göre gruplandırmayı etkinleştirin.

  • Elde edilen tabloya aynı ifadede birden çok kez bakın.


7
Evet. Türetilmiş bir tabloya kendiniz katılamazsınız. Bir CTE'ye kendiliğinden katılmanın yine de sizi 2 ayrı çağrı ile bırakacağına değinmeye değer.
Martin Smith

@Martin - Şaşırdım. Bu ifadeyi yedekleyebilir misiniz?
RichardTheKiwi

@John Teşekkürler, 4guysfromrolla.com/webtech/071906-1.shtml de oldukça faydalı buluyorum
imak

4
@cyberkiwi - Hangi bit? Kendi kendine katılmanın 2 farklı çağrıyı beraberinde getireceğini mi? Bu cevaptaki örneğe bakın stackoverflow.com/questions/3362043/…
Martin Smith

4
CTE hakkında ilginç bir gerçek. Her zaman CTE birden fazla kez başvurulduğunda CTE NEWID () neden merak etti. select top 100 * into #tmp from master..spt_values order by 1,2,3,4 select A.number, COUNT(*) from #tmp A inner join #tmp B ON A.number = B.number+1 group by A.numbervswith CTE AS (select top 100 * from master..spt_values order by 1,2,3,4) select A.number, COUNT(*) from CTE A inner join CTE B ON A.number = B.number+1 group by A.number
RichardTheKiwi

50

Onları karmaşık sorguları, özellikle karmaşık birleştirmeleri ve alt sorguları ayırmak için kullanıyorum. Onları giderek daha fazla 'sahte görünümler' olarak kullanıyorum ve kafamı sorgulamanın amacına getirmeme yardım ediyorum.

Onlar hakkında tek şikayet onlar yeniden kullanılamaz olmasıdır. Örneğin, aynı CTE'yi kullanabilen iki güncelleme deyimiyle depolanmış bir proc'um olabilir. Ancak CTE'nin 'kapsamı' yalnızca ilk sorgudur.

Sorun şu ki, 'basit örnekler' muhtemelen CTE'lere gerçekten ihtiyaç duymaz!

Yine de çok kullanışlı.


tamam. Bu kavramın etrafında kafama yardımcı olabilecek nispeten karmaşık bir örnekle dava açabilir misiniz?
İmak

28
"Onlar hakkında benim tek şikayet onlar yeniden kullanılamaz olduğu" - Bir CTE bir aday düşünülmelidir yeniden kullanımı istediğiniz VIEW:)
onedaywhen

6
@onedaywhen: Anlaşıldı, ancak bu her zaman rahat olmadığım küresel bir kapsamı ima ediyor. Bazen bir proc kapsamında bir CTE tanımlamak ve onu seçimler ve güncellemeler veya farklı tablolardaki benzer verilerin seçimleri için kullanmak istiyorum.
n8wrl

5
Aynı CTE'ye birden çok kez ihtiyaç duyduğumda, geçici bir tabloya veriyorum ve geçici tabloyu istediğim kadar kullanıyorum.
fandango68

43

Cte'leri kullanmak için iki neden görüyorum.

Where yan tümcesinde hesaplanan bir değeri kullanmak için. Bu benim için türetilmiş bir tablodan biraz daha temiz görünüyor.

İki tablo olduğunu varsayalım: Sorular ve Cevaplar, Questions.ID = Yanıtlar.Question_Id (ve sınav kimliği) ile birleştirildi

WITH CTE AS
(
    Select Question_Text,
           (SELECT Count(*) FROM Answers A WHERE A.Question_ID = Q.ID) AS Number_Of_Answers
    FROM Questions Q
)
SELECT * FROM CTE
WHERE Number_Of_Answers > 0

Soruların ve cevapların bir listesini almak istediğim başka bir örnek. Yanıtların sonuçlardaki sorularla gruplandırılmasını istiyorum.

WITH cte AS
(
    SELECT [Quiz_ID] 
      ,[ID] AS Question_Id
      ,null AS Answer_Id
          ,[Question_Text]
          ,null AS Answer
          ,1 AS Is_Question
    FROM [Questions]

    UNION ALL

    SELECT Q.[Quiz_ID]
      ,[Question_ID]
      ,A.[ID] AS  Answer_Id
      ,Q.Question_Text
          ,[Answer]
          ,0 AS Is_Question
        FROM [Answers] A INNER JOIN [Questions] Q ON Q.Quiz_ID = A.Quiz_ID AND Q.Id = A.Question_Id
)
SELECT 
    Quiz_Id,
    Question_Id,
    Is_Question,
    (CASE WHEN Answer IS NULL THEN Question_Text ELSE Answer END) as Name
FROM cte    
GROUP BY Quiz_Id, Question_Id, Answer_id, Question_Text, Answer, Is_Question 
order by Quiz_Id, Question_Id, Is_Question Desc, Name

10
İlk örneğiniz yalnızca CTE yerine iç içe bir sorgu kullanmak için basitleştirilemez mi?
Sam

2
Her iki örnek de olabilir.
Manachi

3
İlkini CTE olmadan eklemeliydiniz, o zaman ikincisinin neden yararlı olduğu hemen anlaşılıyor.
17'de Ufos

HAVINGaltSELECT
William Entriken

21

CTE'yi kullanmak için yararlı bulduğum senaryolardan biri, bir veya daha fazla sütuna dayalı DISTINCT veri satırları almak, ancak tablodaki tüm sütunları döndürmek istediğiniz zamandır. Standart bir sorgu ile, önce ayrı değerleri geçici bir tabloya dökmeniz ve ardından sütunların geri kalanını almak için bunları orijinal tabloya eklemeyi denemeniz gerekebilir veya sonuçları döndüren son derece karmaşık bir bölüm sorgusu yazabilirsiniz. tek seferde ancak büyük olasılıkla okunamayacak ve performans sorununa neden olacaktır.

Ancak CTE kullanarak (Kaydın ilk örneğini seçme konusunda Tim Schmelter tarafından yanıtlandığı gibi )

WITH CTE AS(
    SELECT myTable.*
    , RN = ROW_NUMBER()OVER(PARTITION BY patientID ORDER BY ID)
    FROM myTable 
)
SELECT * FROM CTE
WHERE RN = 1

Gördüğünüz gibi, bu okunması ve bakımı çok daha kolaydır. Ve diğer sorgulara kıyasla, performansta çok daha iyidir.


16

Bir CTE'yi tek bir sorgu için kullanılan görünümün yerine düşünmek belki de daha anlamlı. Ancak, resmi bir görünümün ek yükünü, meta verilerini veya kalıcılığını gerektirmez. Gerektiğinde çok yararlı:

  • Özyinelemeli bir sorgu oluşturun.
  • Sorgunuzda CTE'nin sonuç kümesini birden çok kez kullanın.
  • Özdeş alt sorguların büyük yığınlarını azaltarak sorgunuzda netliği artırın.
  • CTE'nin sonuç kümesinde türetilen bir sütuna göre gruplandırmayı etkinleştir

İşte oynamak için bir kes ve yapıştır örneği:

WITH [cte_example] AS (
SELECT 1 AS [myNum], 'a num' as [label]
UNION ALL
SELECT [myNum]+1,[label]
FROM [cte_example]
WHERE [myNum] <=  10
)
SELECT * FROM [cte_example]
UNION
SELECT SUM([myNum]), 'sum_all' FROM [cte_example]
UNION
SELECT SUM([myNum]), 'sum_odd' FROM [cte_example] WHERE [myNum] % 2 = 1
UNION
SELECT SUM([myNum]), 'sum_even' FROM [cte_example] WHERE [myNum] % 2 = 0;

Zevk almak


7

Bugün, SQL Server 2005'te tanıtılan ve sonraki sürümlerde de kullanılabilen yeni bir özellik olan Ortak tablo ifadesi hakkında bilgi edineceğiz.

Ortak tablo İfadesi: - Ortak tablo ifadesi geçici bir sonuç kümesi olarak tanımlanabilir veya başka bir deyişle, SQL Server'daki görünümlerin yerine kullanılabilir. Ortak tablo ifadesi, yalnızca tanımlandığı ifade kümesinde geçerlidir ve diğer oturumlarda kullanılamaz.

CTE bildirme sözdizimi (Ortak tablo ifadesi): -

with [Name of CTE]
as
(
Body of common table expression
)

Bir örnek verelim: -

CREATE TABLE Employee([EID] [int] IDENTITY(10,5) NOT NULL,[Name] [varchar](50) NULL)

insert into Employee(Name) values('Neeraj')
insert into Employee(Name) values('dheeraj')
insert into Employee(Name) values('shayam')
insert into Employee(Name) values('vikas')
insert into Employee(Name) values('raj')

CREATE TABLE DEPT(EID INT,DEPTNAME VARCHAR(100))
insert into dept values(10,'IT')
insert into dept values(15,'Finance')
insert into dept values(20,'Admin')
insert into dept values(25,'HR')
insert into dept values(10,'Payroll')

Ben iki tablo çalışan ve Borç yarattık ve her tabloya 5 satır ekledi. Şimdi bu tablolara katılmak ve daha fazla kullanmak için geçici bir sonuç kümesi oluşturmak istiyorum.

With CTE_Example(EID,Name,DeptName)
as
(
select Employee.EID,Name,DeptName from Employee 
inner join DEPT on Employee.EID =DEPT.EID
)
select * from CTE_Example

İfadenin her satırını tek tek ele alıp anlayalım.

CTE'yi tanımlamak için "with" deyimini yazıyoruz, sonra tablo ifadesine bir isim veriyoruz, burada "CTE_Example" olarak isim verdim

Sonra "As" yazıp kodumuzu iki parantez (---) içine alıyoruz, ekteki parantez içinde birden çok tabloyu birleştirebiliriz.

Son satırda, "CTE_Example'den * Seç *" i kullandım, kodun son satırında Ortak tablo ifadesine atıfta bulunuyoruz, bu yüzden şunu söyleyebiliriz: toplu iş ve CTE veritabanında kalıcı bir nesne olarak depolanmaz. Ama bir manzara gibi davranıyor. CTE'de sil ve güncelle ifadesi yapabiliriz ve bu CTE'de kullanılan referans alınan tablo üzerinde doğrudan etkisi olacaktır. Bu gerçeği anlamak için bir örnek alalım.

With CTE_Example(EID,DeptName)
as
(
select EID,DeptName from DEPT 
)
delete from CTE_Example where EID=10 and DeptName ='Payroll'

Yukarıdaki ifadede CTE_Example öğesinden bir satır siliyoruz ve CTE'de kullanılan "DEPT" referans tablosundan veri silinecek.


Hala anlamýyorum. Bu ile DEPT'den tamamen aynı koşulda silmek arasındaki fark nedir? Hiçbir şeyi kolaylaştırıyor gibi görünmüyor.
Holger Jakobs

Lütfen yanılıyorsam beni düzeltin, ancak yürütme planı farklı olabilir ve bence bu Neeraj'ın amacı, aynı hedefe ulaşmak için birçok yol var, ancak bazılarının duruma bağlı olarak diğerlerine göre avantajları olacak. Örneğin, bazı durumlarda bir CTE'yi DELETE FROM deyimi üzerinden okumak daha kolay olabilir, bunun tersi diğerlerinde de geçerli olabilir. Performans artabilir veya kötüleşebilir. vb.
WonderWorker

7

"Düzenli bir güncelleme" gerçekleştirmek istediğinizde çok kullanışlıdır.

MS SQL, UPDATE ile ORDER BY kullanmanıza izin vermez, ancak CTE'nin yardımıyla bunu şu şekilde yapabilirsiniz:

WITH cte AS
(
    SELECT TOP(5000) message_compressed, message, exception_compressed, exception
    FROM logs
    WHERE Id >= 5519694 
    ORDER BY Id
)
UPDATE  cte
SET     message_compressed = COMPRESS(message), exception_compressed = COMPRESS(exception)

Daha fazla bilgi için buraya bakın: ms sql kullanarak güncelleme ve sipariş verme


0

Henüz belirtilmeyen noktalardan biri hızdır . Eski bir cevaplı soru olduğunu biliyorum, ama bunun doğrudan yorum / cevabı hak ettiğini düşünüyorum:

Türetilmiş tablolarla aynı şekilde yapılabildiği için gereksiz görünüyorlar

CTE'yi ilk kullandığımda kesinlikle hızıyla hayrete düştüm. CTE için çok uygun olan bir ders kitabındaki gibi bir durumdu , ancak şimdiye kadar CTE kullandığım tüm okurlarda önemli bir hız artışı oldu. İlk sorgumun türetilmiş tablolarla karmaşık olması, yürütülmesi uzun dakikalar aldı. CTE ile saniyeler kesir aldı ve beni şok etti, hatta mümkün bile.


-4
 ;with cte as
  (
  Select Department, Max(salary) as MaxSalary
  from test
  group by department
  )  
  select t.* from test t join cte c on c.department=t.department 
  where t.salary=c.MaxSalary;

bunu dene

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.