# Datahierarkier: Att hantera hierarkiska data med EF Core och PostgreSQL | ( | Übersicht

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

## Inleiding

Hierarkiska data finns överallt i mjukvaruentwicklungen.:, trådlösa kommentarer,, organisationella diagram,, filesystem,, produktkategorier, och forumdiskussionar.. Den eviga frågan om ", hur lagrar jag ett träd i en relationell databas? ?" har hemsökt utvecklare ända sedan SQLs tidiga tid. [först skrev om det här ämnet tillbaka i 2004](/blog/1188), och de fundamentala utmaningarna är desamma idag

### Varför är hierarkier svåra i SQL

Här är det fundamentala problemet. **relationella databaser tänker i sets, inte träd**. Som Joe Celko förklarar i sin fantastiska bok [Att tänka i sets](https://www.amazon.com/Joe-Celkos-Thinking-Sets-Management/dp/0123741378), SQL fungerar på hela tabeller , inte enskilda rader

När du skriver en SQL-fråga, så funkar databasen på *rader*. Det ' är lysande när det gäller operationer som | " hittar alla order över ♫ 100" | eller ♪ " | för att knyta ihop kunderna till sina inköp |" | ♪- | dessa är uppbyggda operationer, som passar naturligt med hur bord fungerar |

Men en hierarki är *rekursiv*. För att hitta alla avkomma från en nod, måste du

1. Hitta de närmaste barnen
2. För varje barn, hitta *deras* barn
3. upprepa tills du' har passerat hela underträdet

Den här rekursiva traversalen kartlägger inte på ett naturligt sätt operationer. Du kan uttrycka alla avkomman i vilken djup som helst i en enda SQL-sats utan att behöva något.

- [Rekursiv CTE](https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-RECURSIVE) (koppelt till SQL :1999, men beräkningsmässigt dyrt
- Många omloppsresor till databasen
- En smart denormalisering som förutsätter relationer

Varje tillvägagångssätt i denna serie representerar en annan handel.-, bortsättning mellan skrivande komplexitet, ,, läskomplexitet och lagernad överhuvudtaget.., det finns inga gratis luncher. [Träd och hierarkier i SQL för smarta](https://www.amazon.com/Hierarchies-Smarties-Kaufmann-Management-Systems/dp/0123877334), som täcker alla dessa tillvägagångssätt i djup

[TOC]

## Det exempel: Threaded Comments

För att göra jämförelserna konkreta, så använder vi threadade kommentarer som vårt körande exempel.

```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
```

Vi behöver stödja dessa operationer

- **få alla kommentarer till en artikel** (med trådsstruktur bevarad
- **få alla förfäder** av en kommentar (broadcrumb trailM SK1
- **få alla barnbarn** av en kommentar (omfattande underträde)
- **Lägg till en ny kommentar** ( som ett svar på en existerande kommentar
- **Skräp en underträd** (avlägsna en kommentar och alla svar
- **Flytta en underträd** (revera en kommentar - mer sällsynt

## De fem tillvägagångssätten

Varje tillvägagångssätt är detaljerat omfattat i sin egen artikel.

---


### 1. Närliggande lista Parentreferens

**[Ta upp hela artikeln](/blog/efcore-hierarchical-data-adjacency)**

Den enklaste och mest intuitiva metoden

**Hur det fungerar:** Varje kommentarer har en nullable `ParentCommentId` column. Rootkommentar har NULL, svar pekar på sin moder

**bäst för:** Obekväma hierarkier ( mindre än 5-6 nivåer), frekvent subträds rörelserM SK3 när du vill att EF Coren ' navigerings egenskaperna ska fungera naturligt

**Världshandel** Att få förfäder eller ättlingar kräver rekursiv CTE eller flera frågeställningar. enkelt att skriva , potentiellt långsamt att läsa djupa träd

---


### 2. Slut جدول

**[Ta upp hela artikeln](/blog/efcore-hierarchical-data-closure)**

Beräknar och lagrar varje förfader i en separat tabell.Istället för att lista ut relationerna vid querytid lagrar vi dem explicit med deras djup.

**Hur det fungerar:** En separat `CommentClosure` bordsbutiker ( förfader _id, efterfädernM SK3id~, djupttonningMska5 för varje parMske6 Berättelse mska7 under Berättelsen mmska8 via Berättande MMska9 skulle ha inskrywingar: มsK11 | | (3,4,1), | (4,4,0).

**bäst för:** Läsa- tunga tillämpningar , när du behöver ställa frågor vid godtyckliga djuper

**Världshandel** Mer komplexa insertioner ( måste lägga till schließningar ), lager växer med djupet M SK2 rörliga underträd kräver återbyggande schließningarna

---


### 3. Materialiserad väg

**[Ta upp hela artikeln](/blog/efcore-hierarchical-data-path)**

Speicherar hela ursprunget som en avgränsad raden. Tänk på det som att lagra hela postadressen snarare än bara namnen på gatan

**Hur det fungerar:** Varje kommentarer har en `Path` raden som "1/3/7" betyder | " | rot är |1, | förälder är ♫ 3, | det här är ♪ 7". | Förfäder kan analyseras från raden | ; | avkomman som hittas med LIKE-fragen

**bäst för:** Breadcrumb generation, när förfäderna är efterfrågade mer än ättlingarn

**Världshandel** LIKE-sökningar kan vara långsamma utan korrekta index.

---


### 4. Nested Sets

**[Ta upp hela artikeln](/blog/efcore-hierarchical-data-nested)**

Speglar varje nod till vänster och höger gränsnummer från en djup *mellan* moderens gränser

**Hur det fungerar:** Varje kommentarer har `Left` och `Right` värden. En nod med L=4, RMSC2 innehåller alla noder där M SK3 < vänster och höger < | | 7. Descendants kan hittas med enkla intervallsökningar

**bäst för:** Läsa- tunga , skriva- har sällan skenar som kategoriträdeMSC3 Utmärkt för M SK4 få hela underträdet i uppvisningsordning

**Världshandel** Inseror kräver att uppdatera många rader. ( ändra alla värden för att göra plats.

---


### 5. PostgreSQL ltree

**[Ta upp hela artikeln](/blog/efcore-hierarchical-data-ltree)**

PostgreSQL' den inhemska förnyelsen till hierarkiska data . Likt materialiserade vägar men med databasen - nivåoptimering och kraftfull mönstermatching

**Hur det fungerar:** använder `ltree` datatyp med vägar som "1.3.7". GiST-index gör det möjligt att utföra effektiva förfädernas sökningar med hjälp av operatörer som `@>` och `<@`.

**bäst för:** PostgreSQL- endast tillämpningar där kvaliteten spelar roll , när du behöver mönstermatchande queryr

**Världshandel** PostgreSQL- endast , lägger till database-extensions beroenden. Nota [Npgsql-leverantör supporterar LINQ-översättningar](https://www.npgsql.org/efcore/mapping/translations.html#ltree-functions) för ltree via `LTree` typ, fast rekursiv CTE kräver fortfarande rå SQL

---


## sammanfattning av jämförelse

| Omgångssätt | | | Insert ♫ | | Query Subtree ♫ ♫ Query Ancestors ♫
|----------|--------|---------------|-----------------|--------------|---------|-----------------|
| **Närliggande lista** | OM SK1 | O≥(n) med CTE | O×(dx) medCTE ♫ | ♫ O× (1) ♫ ♫| ♫ Minimalt ♫
| **Slut Tabellen** | OM SK1d | O~(1) ♪ ♪ | ♪ O^(1) ♪
| **Materialiserad väg** |* | OM SK1 | O~(s) ♫ ♫ | ♫ O~( ♫ d~) ♫ per nod ♫
| **Nesterade insättningar** | OM SK1n | O~(1) ♫ ♫ | ♫ O^(1) ♫
| **träd** | OM SK1 | O~(1) ♫ ♫ | ♫ O~ (1) ♫ `LTree` typ) M SK1

*n = totala noder , d = djupt , s = underträds storlek*
*Med rätt index

## Beslutsdiagramm

```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
```

## Verklig-World Choice: Denna blogg

Denna blogg använder **Slut Tabellen** tillvägagångssätt för kommentarer

1. kommentarer läsers mycket oftare än skrivna
2. Vi behöver visa nesterade kommentarer trådar effektivt
3. Depth-begränsande frågor är viktiga för prestationen (visar vi upp på nivåer ♫ 5 ♫ djupt ♫
4. Att flytta kommentarer är ovanligt. (moderatorer behöver ibland göra detta.

Se [Del 1.2: Slutbrädan](/blog/efcore-hierarchical-data-closure) för fulla implementeringsdetails.

## Seri Navigation

- **Del 1: Übersicht** (den här artikeln )
- [Del 1.1: Närliggande lista](/blog/efcore-hierarchical-data-adjacency) - Enkela moderreferenser
- [Del 1.2: Slutbrädan](/blog/efcore-hierarchical-data-closure) - Förberäknade relationer
- [Del 1.3: Materialiserad väg](/blog/efcore-hierarchical-data-path) - Pathsträngar
- [Del 1.4: Nested Sets](/blog/efcore-hierarchical-data-nested) - vänsterM SK1 högra gränser
- [Del 1.5: ltree](/blog/efcore-hierarchical-data-ltree) - PostgreSQL-nativextension
- Del 2: Raw SQL and Dapper (coming soon)