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

<!--category-- Entity Framework, PostgreSQL, EF Hierarchies -->
<datetime class="hidden">2025-12-06T09:00</datetime>

## 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](/blog/1188), ja perushaasteet ovat edelleen samat.

### Miksi hierarkiat ovat vaikeita SQL:ssä

Tässä on perustavanlaatuinen ongelma: **suhteelliset tietokannat ajattelevat seteissä, eivät puissa**Kuten Joe Celko kertoo erinomaisessa kirjassaan [Mielenkiintoisia sarjoja](https://www.amazon.com/Joe-Celkos-Thinking-Sets-Management/dp/0123741378), SQL toimii kokonaisilla pöydillä, ei yksittäisillä riveillä.

Kun kirjoitat SQL-kyselyn, tietokantamoottori toimii *rivisarjat*Se 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 *rekursiivinen*Lö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](https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-RECURSIVE) (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](https://www.amazon.com/Hierarchies-Smarties-Kaufmann-Management-Systems/dp/0123877334), joka kattaa kaikki nämä lähestymistavat perusteellisesti.

[TOC]

## 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ä:

```mermaid
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](/blog/efcore-hierarchical-data-adjacency)**

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](/blog/efcore-hierarchical-data-closure)**

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](/blog/efcore-hierarchical-data-path)**

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](/blog/efcore-hierarchical-data-nested)**

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](/blog/efcore-hierarchical-data-ltree)**

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ä](https://www.npgsql.org/efcore/mapping/translations.html#ltree-functions) 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

```mermaid
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](/blog/efcore-hierarchical-data-closure) täydelliset täytäntöönpanotiedot.

## Sarjanavigointi

- **Osa 1: Yleiskatsaus** (tämä artikla)
- [Osa 1.1: Liitännäisluettelo](/blog/efcore-hierarchical-data-adjacency) - Yksinkertaisia vanhemmuusviitteitä
- [1.2 osa: Sulkemistaulukko](/blog/efcore-hierarchical-data-closure) - Esilasketut suhteet
- [Osa 1.3: Aineellinen polku](/blog/efcore-hierarchical-data-path) - Polun narut
- [Osa 1.4: Nested Setit](/blog/efcore-hierarchical-data-nested) - Vasen/oikea raja
- [Osa 1.5: puu](/blog/efcore-hierarchical-data-ltree) - PostgreSQL-laajennus
- Osa 2: Raw SQL ja Dapper (tulee pian)