2013-02-05 18 views
5

Yaklaşık 70.000 satır ve iki sütun (her ikisi de VARCHAR(16)): id ve parent_id'dan oluşan bir tablom var.Nesne hiyerarşisinin derinliğini belirlemek için CTE ve T-SQL döngüsü

Belirli bir kaydın "kök" düğümden ne kadar uzakta olduğunu gösteren bir 'derinlik' sütununu doldurmak istiyorum.

örn. Yukarıda çalıştırmak için yaklaşık iki saat sürer benim veri kümesi ile

WITH myCTE(id, depth) AS 
(
    SELECT id, 0 FROM objects where id = 'A' 
    UNION ALL 
    SELECT objects.id, depth + 1 FROM myCTE JOIN objects ON objects.parent_id = myCTE.id 
) 
SELECT id, depth FROM myCTE 

(~ 80.000 satır):

id,parent_id,depth 
A,NULL,0 
B,A,1 
C,A,1 
D,B,2 
E,D,3 

vb

Ben benzer bir soruya this answer dayalı bir sorgu yazarak başladı !

Sonra bir döngü olarak benim sorgu yazdım ve çok daha iyi bir performans var:

ALTER TABLE objects ADD depth INT NULL 
DECLARE @counter int 
DECLARE @total int 
SET @counter = 0 
UPDATE objects SET depth = 0 WHERE id = 'A' 

SELECT @total = COUNT(*) FROM objects WHERE depth IS NULL 

WHILE (@total > 0) 
BEGIN 
    UPDATE objects SET depth = @counter + 1 WHERE parent_id IN (
     SELECT id FROM objects WHERE depth = @counter 
    ) 
    SELECT @total = COUNT(*) FROM objects WHERE depth IS NULL 
    SET @counter = @counter + 1 
END 

Yukarıdaki kod sadece birkaç dakika sürer (ve mevcut tabloya sonuçlar ekleyerek yararlanabileceği)

Sorunun, sonuçların bu sorun için CTE kullanmanın tipik bir yolu olup olmadığını veya gözden kaçırdığım bir şey olup olmadığını açıklayıp açıklamamasıdır. Endeksler, belki? (Şu anda masada hiç yok)

+0

Vay. Benim tecrübemde, bu oldukça tipik olmayan sesler. İkisi arasında bir karşılaştırma görmek için yürütme planlarını açmak zorunda mıyım? – Matt

+1

@Matt - Orta büyüklükteki büyük tablolarda, CTE'nin yineleyici kısmının bir indeks aramasıyla veya [Performansın korkunç bir şekilde bozulmasına neden olabilir] tarafından karşılanabilmesi kritik öneme sahiptir (http://dba.stackexchange.com/q/15596/ 3690) –

cevap

8

parent_id'da bir dizine ihtiyacınız var. Bir CTE yinelemeli kısmı her zaman iç içe geçmiş bir döngüler birleştirme kullanmak ve (Sonuçların stack spool ilave edilir ve satırları LIFO amacıyla tek tek işlenmektedir) o gerekir parent_id bir dizin olmadan

ipuçlarını katılma geçirmeyen olacak iç içe geçmiş ilmeklerin iç tarafındaki tabloyu birkaç kez taramak için. Performans, satır sayısıyla katlanarak azalacaktır.

Sorgunuz olmadan yinelenen sorgularınız, her bir yineleme düzeyi için yalnızca iki kez tabloyu taranan farklı birleştirme türlerini (karma veya birleştirme) kullanabilecektir. Muhtemelen bir sıralamadan kaçınacak yararlı endeksleriniz olmadığından, büyük olasılıkla bu durumda birleştirme hashı olur.

0

HierarchyID veri türünü kullanmayı düşündünüz mü? Hayatını çok daha kolaylaştıracak.

CREATE TABLE Groups.tblHierarchyNode 
(
     NodeID    Int IDENTITY (0,1), 
     NodeHID    HierarchyID NOT NULL, -- DB Hierarchy ID of where I am in a tree 
     HierarchyLevel  AS NodeHID.GetLevel(), -- Numerical level of where I am in tree 
) 

Bunu çoğu zaman hiyerarşik tablolarım için kullanıyorum. Tablo popülasyonunda biraz daha akıllı olmalısınız, ancak raporlar, hiyerarşinin yukarı ve aşağı doğru hareket ettiği, ataların, torunların ve benzerlerinin olduğu gibi bir esinti.