Back to "Datahierarkioita: Hierarkical Datan hallinta EF Corella ja PostgreSQL:llä (Ylikatselu)"

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

EF Hierarchies Entity Framework PostgreSQL

Datahierarkioita: Hierarkical Datan hallinta EF Corella ja PostgreSQL:llä (Ylikatselu)

Saturday, 06 December 2025

Johdanto

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.

Miksi hierarkiat ovat vaikeita SQL:ssä

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:

  1. Etsi välittömät lapset
  2. Jokaiselle lapselle löytyy niiden lapset
  3. Toista, kunnes olet ylittänyt koko suiston.

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:

  • Rekursiivinen CTE (lisänä SQL:1999, mutta laskennallisesti kallis)
  • Useita kierroksia tietokantaan
  • Terävä denormalisointi, joka ennakoi suhteita

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.

Esimerkki: Kiertyneet kommentit

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:

  • Saat kaikki kommentit postitse (jossa on kierteitysrakenne säilytetty)
  • Hae kaikki esi-isät Kommentti (murupolku)
  • Hae kaikki jälkeläiset kommentin (täysosa)
  • Lisää uusi kommentti (vastauksena olemassa olevaan huomautukseen)
  • Poista sukelluspuu (Poista kommentti ja kaikki vastaukset)
  • Siirrä alikulkua (toista kommentti - harvinaisempaa, mutta joskus tarpeellista)

Viisi lähestymistapaa

Jokainen lähestymistapa käsitellään yksityiskohtaisesti sen omassa artikkelissa. Tässä on yhteenveto, jonka avulla voit valita:


1. Adjacency List (vanhemman viite)

Lue koko artikkeli

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.


2. Sulkemistaulukko

Lue koko artikkeli

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.


3. Aineellinen polku

Lue koko artikkeli

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ä.


4. Nested Setit

Lue koko artikkeli

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.


5. PostgreSQL ltree

Lue koko artikkeli

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ä.


Vertailun yhteenveto

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

Päätöksen vuokaavio

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

Real-World Choice: This Blog

Tämä blogi käyttää Sulkemistaulukko kommentteja koskeva lähestymistapa. Perustelut:

  1. Kommentteja luetaan paljon useammin kuin kirjoitettuja
  2. Meidän on näytettävä pesityt kommenttilangat tehokkaasti
  3. Syvyyttä rajoittavat kyselyt ovat tärkeitä suorituskyvyn kannalta (katto 5 tasoa syvällä)
  4. Liikkuvat kommentit ovat harvinaisia (moderaattoreiden on joskus tehtävä näin)

Katso 1.2 osa: Sulkemistaulukko täydelliset täytäntöönpanotiedot.

Sarjanavigointi

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.