This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Saturday, 06 December 2025
Hierarkkista dataa on ohjelmistokehityksessä kaikkialla: kierrettyjä kommentteja, organisaatiokaavioita, tiedostojärjestelmiä, tuotekategorioita ja foorumikeskusteluja. Ikuinen kysymys siitä, "miten varastoin puun suhteelliseen tietokantaan?" on ahdistanut kehittäjiä SQL:n alkupäivistä lähtien, ja rehellisesti sanottuna ei ole vielä yhtä ainoaa vastausta, joka tekisi kaikki tyytyväisiksi. kirjoitti aiheesta ensimmäisen kerran jo vuonna 2004, ja perushaasteet ovat edelleen samat.
Tässä on perustavanlaatuinen ongelma: suhteelliset tietokannat ajattelevat seteissä, eivät puissaKuten Joe Celko kertoo erinomaisessa kirjassaan Mielenkiintoisia sarjoja, SQL toimii kokonaisilla pöydillä, ei yksittäisillä riveillä.
Kun kirjoitat SQL-kyselyn, tietokantamoottori toimii rivisarjatSe on loistava toiminnassa, kuten "löydä kaikki tilaukset yli 100" tai "liitä asiakkaat ostoksilleen" - nämä ovat asetettuja toimintoja, jotka sopivat luonnollisesti siihen, miten pöydät toimivat. Tuloksena on aina tasainen rivistö.
Mutta hierarkia on luonnostaan rekursiivinenLöytääksesi kaikki solmun jälkeläiset, sinun täytyy:
Tämä rekursiivinen väylä ei luonnollisestikaan kartoita operaatioita. Et voi ilmaista "anna minulle kaikki jälkeläiset syvyydessä" yhdellä yksinkertaisella SQL-lausunnolla ilman kumpaakaan:
Jokainen tämän sarjan lähestymistapa on erilainen kuin kirjoitusten monimutkaisuuden, lukemisen monimutkaisuuden ja tallennuksen välillä. Ei ole ilmaista lounasta - vaihdat aina yhden hinnan toiseen. Lopullinen viittaus aiheesta löytyy Joe Celkon osoitteesta SQL:n puita ja hierarkioita älykkäille, joka kattaa kaikki nämä lähestymistavat perusteellisesti.
Vertailujen konkretisoimiseksi käytämme kierteisiä kommentteja juoksuesimerkkinämme, jota juuri tämä blogi käyttää. Kommenttiketju voi näyttää tältä:
flowchart TD
subgraph Post["Post: How to Deploy Docker Containers"]
C1["Comment 1: Great article!<br/>depth 0"]
C2["Comment 2: Thanks!<br/>depth 1"]
C3["Comment 3: Very helpful indeed<br/>depth 1"]
C4["Comment 4: Agreed!<br/>depth 2"]
C5["Comment 5: What about Kubernetes?<br/>depth 0"]
C6["Comment 6: That's covered in part 2<br/>depth 1"]
end
C1 --> C2
C1 --> C3
C3 --> C4
C5 --> C6
style Post stroke:#10b981,stroke-width:2px
style C1 stroke:#6366f1,stroke-width:2px
style C2 stroke:#8b5cf6,stroke-width:2px
style C3 stroke:#8b5cf6,stroke-width:2px
style C4 stroke:#a855f7,stroke-width:2px
style C5 stroke:#6366f1,stroke-width:2px
style C6 stroke:#8b5cf6,stroke-width:2px
Meidän on tuettava näitä operaatioita:
Jokainen lähestymistapa käsitellään yksityiskohtaisesti sen omassa artikkelissa. Tässä on yhteenveto, jonka avulla voit valita:
Yksinkertaisin ja intuitiivisin lähestymistapa. Jokainen rivi tallentaa viittauksen emosolmukkeeseensa. Se on se, mihin useimmat kehittäjät ensin pyrkivät, koska se kartoittaa luonnollisesti, miten ajattelemme hierarkioita.
Miten se toimii: Jokaisessa kommentissa on pätemätön ParentCommentId kolumni. Root comments have NULL, vastaukset viittaavat heidän vanhempaansa.
Paras: Piileviä hierarkioita (alle 5-6 tasoa), toistuvia alikulkuja, kun haluat EF Coren navigointiominaisuuksien toimivan luonnollisesti.
Vastoinkäymiset: Esi-isien tai jälkeläisten saaminen vaatii rekursiivisia CTE-tietoja tai useita kyselyitä. Yksinkertaista kirjoittamista, mahdollisesti hidasta syväpuiden lukemista.
Laskee ja tallentaa jokaisen esi-isän ja alistuvan suhteen erilliseen taulukkoon. Sen sijaan, että selvittäisimme suhteita kyselyhetkellä, tallennamme ne nimenomaan niiden syvyyteen.
Miten se toimii: Erillinen CommentClosure Pöydät (encestor_id, jälkeläis_id, syvyys) jokaiselle parille. Kommentti 4 Comment 1 via Kommentti 3 sisältää merkinnät: (1,4,2), (3,4,1), (4,4,0).
Paras: Lukevat sovellukset, kun niitä tarvitsee kysellä mielivaltaisissa syvyyksissä, kun niitä liikkuu harvoin.
Vastoinkäymiset: Monimutkaisempia inserttejä (täytyy lisätä sulkemisilmoituksia), varastointi kasvaa syvyyden myötä, siirtopuita joudutaan rakentamaan uudelleen.
Koko syntyperä säilytetään rajattuna merkkijonona. Ajattele, että se säilyttää koko postiosoitteen eikä vain katunimeä.
Miten se toimii: Jokaisessa kommentissa on Path Sarake "1/3/7" tarkoittaa "juuri on 1, emo on 3, tämä on 7". Esi-isät voidaan yhdistää merkkijonoon; jälkeläiset löytyvät samankaltaisilla kyselyillä.
Paras: Murupolvipolvi, kun esi-isiä tiedustellaan enemmän kuin jälkeläisiä, kun halutaan ihmisten luettavissa olevia polkuja vianetsintään.
Vastoinkäymiset: MUIDEN tavoin kyselyt voivat olla hitaita ilman kunnollisia indeksejä, polun naruilla on pituusrajoitukset, ja niiden siirtäminen edellyttää kaikkien jälkipolvien päivittämistä.
Se osoittaa jokaisen solmun vasemman ja oikean reunan numeron syvyyden ensimmäisestä risteyksestä. Kaikilla jälkeläisillä on arvot välillä vanhemman rajat.
Miten se toimii: Jokainen kommentti on Left sekä Right Arvot. Solmupiste, jossa on L=4, R=7, sisältää kaikki solmut, joissa 4 < Vasen ja Oikea < 7. Descendants löytyy yksinkertaisine valikoimakyselyineen.
Paras: Read-heavy, write-harvemmin skenaarioita, kuten kategorian puita. Erinomainen "saada kokonaiset subtreet näyttökuntoon" -kyselyihin.
Vastoinkäymiset: Lisääminen vaatii useiden rivien päivittämistä (vaihda kaikki arvot tilaan), subtreenien siirtäminen on monimutkaista. Se ei sovi usein kirjoittaville.
PostgreSQL:n natiivilaajennus hierarkkiseen dataan. Kuten materialisoidut polut, mutta tietokannan optimointi ja tehokas kuvioiden täsmäytys.
Miten se toimii: Käyttää ltree Tietotyyppi, jolla on polkuja kuten "1.3.7". GiST-indeksit mahdollistavat tehokkaat esi-/päättäjäkyselyt, joissa käytetään seuraavanlaisia operaattoreita: @> sekä <@.
Paras: PostgreSQL-käyttökohteet, joissa suorituksella on merkitystä, kun tarvitaan kuvioita vastaavia kyselyitä, kun halutaan parasta mahdollista polkua.
Vastoinkäymiset: PostgreSQL- vain, lisää tietokannan laajennusriippuvuutta. Huomautus: Npgsql-toimittaja tukee LINQ-käännöksiä ltreeniä varten LTree Type, vaikka rekursiiviset CTE:t vaativat vielä raakaa SQL:tä.
Lähestymistapa Lisää Query Subtree Query Esi-isät Siirrä Subtree Storage EF Core Support
|----------|--------|---------------|-----------------|--------------|---------|-----------------|
| Näkemysluettelo O(1) O(n) CTE:n kanssa O(d) CTE:n kanssa O(1) Minimaalin kanssa Excellent
| Sulkemistaulukko O(d) O(1) O(s x) O(s x d) O(n x d) Hyvä
| Aineellinen polku O(1) O(1)* O(1) O(s) O(d) solmukohta Hyvä
| Nested-sarjat O(n) O(1) O(n) Minimaalinen hyvä
| lehtipuu O (1) O (1) O(t) O(t) O(t) / solmukohta Hyvä (v) LTree tyyppi)
n = kokonaissolmut, d = syvyys, s = alapuun koko *Oikealla hakemistolla
flowchart TD
Start([Start]) --> Q1{Shallow tree?<br/>< 5 levels}
Q1 -->|Yes| Q2{Need EF Core<br/>navigation properties?}
Q2 -->|Yes| AL[Adjacency List]
Q2 -->|No| Q3{Performance<br/>critical?}
Q3 -->|No| AL
Q3 -->|Yes| MP[Materialised Path]
Q1 -->|No| Q4{Read-heavy?}
Q4 -->|Yes| Q5{Writes rare?}
Q5 -->|Yes| NS[Nested Sets]
Q5 -->|No| CT[Closure Table]
Q4 -->|No| Q6{PostgreSQL only?}
Q6 -->|Yes| LT[ltree]
Q6 -->|No| Q7{Need breadcrumbs?}
Q7 -->|Yes| MP
Q7 -->|No| CT
style Start stroke:#10b981,stroke-width:2px
style AL stroke:#6366f1,stroke-width:2px
style MP stroke:#8b5cf6,stroke-width:2px
style NS stroke:#ec4899,stroke-width:2px
style CT stroke:#f59e0b,stroke-width:2px
style LT stroke:#14b8a6,stroke-width:2px
Tämä blogi käyttää Sulkemistaulukko kommentteja koskeva lähestymistapa. Perustelut:
Katso 1.2 osa: Sulkemistaulukko täydelliset täytäntöönpanotiedot.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.