PostgreSQL - ağaç organizasyon

6 Cevap php

Ben bir proje wich üzerinde çalışıyorum kimliği, veli, başlık tablo olarak düzenlenmiş kategorilerde bir ağaç gerektirir. Postgres (Kök kategoriler parent = 0 varsa ve tam bir ağaç) Hangi kategori ve alt kategorileri almak için en iyi yolları nelerdir? Ben saf bir veritabanı çözümü için arıyorum, ama Ruby ve PHP için bir yol var - eğer çok büyük olacaktır.

Ana hedefi güncelleme / ekleme / silme hızı için 'bu tabloda neden veri kritik değildir seçin hükümlerinin hızıdır.

UDP: Ayrıca yol arama orada olacak, ben kök kategori mevcut köşenin (kategori) için yol anlamına gelir.

6 Cevap

kategori ve alt kategorilerini almak

Sadece alt öğeler sınırlı bir derinliği varsa, kendi kendine katılmak, örneğin kullanarak bunu yapabilirsiniz. iki düzeyden derin:

SELECT *
FROM categories AS child
LEFT JOIN categories AS parent ON parent.id=child.parent
LEFT JOIN categories AS grandparent ON grandparent.id=parent.parent
WHERE child.id=(id) OR parent.id=(id) OR grandparent.id=(id);

Bir 'ebeveyn-id-yabancı-anahtar' tipi şema üzerinde standart SQL kullanarak bir keyfi derinlik hiyerarşi için bunu yapamaz.

Bazı DBMSs çeşitli yollarla böyle bir şeye izin standart olmayan hiyerarşik araçları sağlamak, ancak çapraz DBMS uyumlu kod sopa istiyorsanız hiyerarşileri temsil iyi modellerinden biri için şema rejig gerekir. Iki popüler olanları:

  • Nested Set. Mağazalar hedef tablo iki sütun içinde ağacın bir derinlik ilk arama (hedef açık sipariş varsa zaten olacak olan biri) temsil eden doğrusal bir sipariş.

  • Adjacency Relation. Ayrı bir birleştirme tablosunda saklar her ata / soyundan çifti.

Orada her yaklaşımın avantajları ve dezavantajları, ve ekleme / silme / taşıma-pozisyon işlemleri pahalı çeşitli türleri vardır nasıl etkileyebilir sayısız türevleri (örn. seyrek iç içe set numaralama, AR 'mesafe'). Şahsen ben AR az fazlalık içerir varsayılan olarak basitleştirilmiş iç içe dizi modeline doğru çekilmek eğilimindedir.

"ltree" contrib modülü bir göz atın.

Ben dişli yorum için iyi bir uyum olup olmadığını görmek için bir PostgreSQL contrib modülü olan ltree ile oynamak oldum. Sen yolunu depolayan tabloda bir sütun oluşturmak ve bunun üzerine bir ltree dizin oluşturmak .. Daha sonra bu gibi sorguları gerçekleştirebilirsiniz:

 ltreetest=# select path from test where path ~ '*.Astronomy.*';
                     path                      
-----------------------------------------------
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology
 Top.Collections.Pictures.Astronomy
 Top.Collections.Pictures.Astronomy.Stars
 Top.Collections.Pictures.Astronomy.Galaxies
 Top.Collections.Pictures.Astronomy.Astronauts

Ben bu ekler, güncelleştirmeleri veya silmeleri gibi şeyler ile ne kadar iyi performans belirlemek için yeterli onunla çevresinde oynanır değil. Ben bir silme gibi görünecektir varsayalım:

DELETE FROM test WHERE path ~ '*.Astronomy.*';

Ben bir dişli comment tablo gibi görünebilir, düşünüyorum:

CREATE SEQUENCE comment_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 78616
  CACHE 1;

CREATE TABLE comments (
comment_id int PRIMARY KEY,
path ltree,
comment text
);

CREATE INDEX comments_path_idx ON comments USING gist (path);

: Bir ekleme kabaca (ve denenmemiş-ly) gibi görünecektir

CREATE FUNCTION busted_add_comment(text the_comment, int parent_comment_id) RETURNS void AS
$BODY$
DECLARE
    INT _new_comment_id; -- our new comment_id
    TEXT _parent_path;   -- the parent path
BEGIN
    _new_comment_id := nextval('comment_id_seq'::regclass);
    SELECT path INTO _parent_path FROM comments WHERE comment_id = parent_comment_id;

    -- this is probably busted SQL, but you get the idea... this comment's path looks like
    --   the.parent.path.US
    --
    -- eg (if parent_comment_id was 5 and our new comment_id is 43):
    --  3.5.43
    INSERT INTO comments (comment_id, comment, path) VALUES (_new_comment_id, the_comment, CONCAT(_parent_path, '.', _new_comment_id));

END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

Ya da bir şey. Temelde yolu sadece tüm birincil anahtarlar oluşan bir hiyerarşi olduğunu.

Ben durumun bu tür iç içe kümesi modelinde düşkün oldum. Güncelleştirmeler ve Ekler biraz zor olabilir, ama seçer genellikle çok kısa ve hızlı. Bir düğümün ebeveyni gerçek bir başvuru eklerseniz performansı daha iyi olabilir (o ortadan kaldıracak bir bazı durumlarda katılmak. Ayrıca ChildNodes doğal bir sıralama içerir.

Geçerli düğüm ve tüm çocuklar için tipik bir sorgu gibi görünecektir:

select name
from nestedSet c inner join nestedSet p ON c.lft BETWEEN p.lft AND p.rgt
where p.id = 1
order by lft

Birkaç iyi yerleştirilmiş group by hükümler net de olacak size ağacı hakkında bazı hızlı istatistikleri.

Geçmişte benim için iyi çalıştı Raylar için acts_as_tree eklenti vardır. 15.000 civarında düğümleri - Gerçi oldukça küçük bir ağaç vardı.

Sadece eklemek, yazı Managing Hierarchical Data in MySQL ağaç manipülasyon ve bu örneğin SQLs dahil komşuluk listesi Modeli ve iç içe Set Modelleri iyi bir açıklama vardır.

Bir RDBMS hiyerarşileri zor bir konudur. Ben bir gün satın almak ve okumak için benim dilek listesinde Joe Celko’s Trees and Hierarchies in SQL for Smarties var.