Hiyerarşik yapılarla çalışma, ilişkisel veritabanlarının klasik bir konusudur. Örnekler: dizinler, ağaçsal menüler, organizasyon yapılarını içerir.
Konuya Giriş: Veritabanları, tablodan oluşmuş bir modeldir. Ağaçsal verilere yönelik birkaç yaklaşım vardır ve her birinin kendine özgü artıları ve eksileri bulunmaktadır.
Sorun: Standart parent_id modeli, yavaş tekrarlayan SELECT'lere yol açar. Gerçek görevler (tüm çocukların aranması, yollar, alt ağaçların sayılması) optimizasyon gerektirir.
Çözüm:
Materialized Path için örnek kod (PostgreSQL):
CREATE TABLE categories ( id SERIAL PRIMARY KEY, name TEXT, path TEXT ); -- Path'in saklanma örneği: '1/2/5/' (kök/alt kategori/mevcut) SELECT * FROM categories WHERE path LIKE '1/2/%'; -- 2'nin tüm çocukları
Nested Sets için örnek kod:
CREATE TABLE nested_categories ( id SERIAL PRIMARY KEY, name TEXT, lft INT NOT NULL, rgt INT NOT NULL ); -- Alt düğümler SELECT * FROM nested_categories WHERE lft > 2 AND rgt < 15;
Anahtar özellikler:
Herhangi bir derinlikte tüm çocukları ele almak için sadece parent_id ile iç içe SELECT kullanabilir miyiz?
Bu, küçük derinlikler için çalışır. Rekürsif arama için ya rekürsif CTE (WITH RECURSIVE) ya da daha karmaşık şemalar gereklidir; çünkü basit JOIN'ler büyük sayıda sorguya ve kötü performansa yol açar.
Örnek:
WITH RECURSIVE cte AS ( SELECT id, parent_id, name FROM categories WHERE id = 1 UNION ALL SELECT c.id, c.parent_id, c.name FROM categories c INNER JOIN cte ON c.parent_id = cte.id ) SELECT * FROM cte;
Moleküler ağaç (Nested Sets) neden alt ağaçları hızlı çıkarıyor ama düğümlerin eklenmesi/silinmesi yavaş?
Ağacın değiştirilmesi gerektiğinde, lft/rgt'yi birçok satırda hemen değiştirmek gerekir (değişim adımı — ekleme/silmeden büyük tüm değerler). Okuma için ideal bir yaklaşım ama sık sık değişiklikler için başka yöntemler kullanılmalıdır.
Closure Table'ı ne zaman kullanmalıyız, parent_id veya materialized path yerine?
Closure Table, herhangi bir seviyedeki çocuklara sık sık yapılan sorgular ve düzenli ilişki hesaplamaları için mükemmel şekilde çalışır. Eksisi, daha fazla alan gerektirmesidir.
Örnek:
CREATE TABLE closure ( ancestor INT, descendant INT, depth INT );
Bir çevrimiçi mağazada kategoriler, parent_id aracılığıyla uygulanmıştır. Tüm iç içe yapılar elle yerleştirilmekte, alt kategorileri arama işlemi iç içe SELECT'lerle yapılmaktadır.
Artılar:
Eksiler:
Materialized path veya closure table kullanılıyor. Tüm iç içe kategori sorguları anındadır, hesaplamalar grup scriptleri ile yapılmaktadır.
Artılar:
Eksiler: