Ağaçlar ve hiyerarşilerle çalışmak, SQL programlamada sık karşılaşılan bir durumdur: ürün katalogları, organizasyon yapıları, menüler. Başlangıçta, çoğu DBMS yalnızca ebeveyn referansı (ParentId) destekliyordu, ancak zamanla alternatif yöntemler – iç içe kümeler ve yollar (Path) ile birlikte CTE aracılığıyla özyinelemeli sorgular (recursive queries) ortaya çıktı.
Sorun, geleneksel Parent-Child yaklaşımının büyük hiyerarşilerde düşük bir hızla çalışmasıdır; 2-3 seviyeden daha derin sorgulamalar, kontrolsüz bir JOIN sayısına yol açar. Daha karmaşık teknikler (Nested Sets, Path Enumeration), kütlevi dolaşmaları hızlandırırken, ekleme/silme işlemlerini zorlaştırır.
Çözüm, belirli bir görev için optimal depolama modelini seçmektir:
Örnek özyinelemeli alt-ağaç araması CTE aracılığıyla:
WITH RecursiveTree AS ( SELECT id, parent_id, name, 0 as level FROM categories WHERE parent_id IS NULL UNION ALL SELECT c.id, c.parent_id, c.name, rt.level + 1 FROM categories c INNER JOIN RecursiveTree rt ON c.parent_id = rt.id ) SELECT * FROM RecursiveTree;
Anahtar özellikler:
Hiyerarşilerin depolanması, sabit derinlikte bir düzeneğin tek bir denormalize edilmiş tablo ile değiştirilmesi mümkün mü?
Sadece derinlik az ve her zaman sabit ise (2-3 seviye) mümkündür; daha sonrasında JOIN'ler yönetilemez hale gelir ve değişikliklerin işlenmesi zorlaşır.
Büyük ağaçlarda CTE "asılı kalmaz" mı – özyineleme derinliği/stack ile ilgili kısıtlamalar var mı?
Evet, çoğu DBMS'de özyineleme derinliği için limitler vardır (örneğin, 100/32767). 1000+ seviye ağaçlar için derinliği manuel yönetmek veya özelleştirilmiş dolaşım/bölme algoritmaları gereklidir.
Nested Sets – tüm sorunların çözümü mü?
Hayır, nested sets anında tüm alt öğeleri bulur, ancak ekleme/silme işlemleri, tüm sol/sağ indekslerinin kütlevi güncellenmesini gerektirir – bu, sık değişikliklerde yavaş bir işlemdir.
Ekleme örneği (basitleştirilmiş):
UPDATE tree SET lft = lft + 2 WHERE lft > @parent_right; UPDATE tree SET rgt = rgt + 2 WHERE rgt >= @parent_right; INSERT INTO tree(id, name, lft, rgt) VALUES(@new_id, @name, @parent_right, @parent_right + 1);
Büyük bir çevrimiçi mağaza, kategori ağacını adjacency list'te sakladı ve menüyü iç içe alt sorgularla oluşturdu. 6+ seviye menüde ana sorgu birkaç dakika sürerken, ağaçtaki herhangi bir değişiklik cascade update'e yol açıyordu. Artılar:
Eksiler:
Statik ağaçlar (kategoriler) için Nested Sets'e geçtik, menü yolları için Path'a geçtik. Dinamik senaryolar için CTE'yi kullandık. Alt öğeleri bulmak anında gerçekleşti, değişiklikleri partiler halinde yaptık. Artılar:
Eksiler: