Back to "Ιεραρχίες Δεδομένων: Διαχείριση Ιεραρχικών Δεδομένων με πυρήνα EF και PostgreSQL (Overview)"

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

Ιεραρχίες Δεδομένων: Διαχείριση Ιεραρχικών Δεδομένων με πυρήνα EF και PostgreSQL (Overview)

Saturday, 06 December 2025

Εισαγωγή

Ιεραρχικά δεδομένα είναι παντού στην ανάπτυξη λογισμικού: κοχλιωτά σχόλια, οργανωτικά διαγράμματα, συστήματα αρχείων, κατηγορίες προϊόντων, και συζητήσεις φόρουμ. Το αιώνιο ερώτημα του "πώς μπορώ να αποθηκεύσω ένα δέντρο σε μια σχεσιακή βάση δεδομένων;" έχει στοιχειωμένο προγραμματιστές από τις πρώτες ημέρες του SQL, και ειλικρινά, δεν υπάρχει ακόμα καμία απάντηση που κάνει τους πάντες ευτυχισμένους. για πρώτη φορά έγραψε για αυτό το θέμα το 2004, και οι θεμελιώδεις προκλήσεις παραμένουν οι ίδιες σήμερα.

Γιατί οι Ιεραρχίες Είναι Δύσκολες στο SQL

Εδώ είναι το βασικό πρόβλημα: σχετικά με τις βάσεις δεδομένων σκέφτονται σε σύνολα, όχι δέντραΌπως εξηγεί ο Τζο Κέλκο στο έξοχο βιβλίο του Σκέψη σε Σύνολα, SQL λειτουργεί σε ολόκληρους πίνακες, όχι μεμονωμένες σειρές.

Όταν γράφετε ένα ερώτημα SQL, ο κινητήρας βάσης δεδομένων λειτουργεί σε Σετ γραμμών. Είναι λαμπρό σε λειτουργίες όπως "βρείτε όλες τις παραγγελίες πάνω από 100" ή "join πελάτες στις αγορές τους" - αυτές είναι set λειτουργίες που ταιριάζουν φυσικά με το πώς λειτουργούν τα τραπέζια. Το αποτέλεσμα είναι πάντα ένα επίπεδο σύνολο γραμμών.

Αλλά μια ιεραρχία είναι εγγενώς αναδρομικόΓια να βρείτε όλους τους απογόνους ενός κόμβου, θα πρέπει να:

  1. Βρείτε τα άμεσα παιδιά
  2. Για κάθε παιδί, βρείτε τους παιδιά
  3. Επαναλάβετε μέχρι να διασχίσετε όλο το υποδέντρο.

Δεν μπορείτε να εκφράσετε "δώστε μου όλους τους απογόνους σε οποιοδήποτε βάθος" σε μια απλή δήλωση SQL χωρίς:

  • Αναδρομικά CTE (προσθήκη στο SQL:1999, αλλά υπολογιστικά ακριβό)
  • Πολλαπλές στρογγυλές εκδρομές στη βάση δεδομένων
  • Έξυπνη απομαλοποίηση που προτρέπει τις σχέσεις

Κάθε προσέγγιση σε αυτή τη σειρά αντιπροσωπεύει μια διαφορετική ανταλλαγή μεταξύ γράψτε πολυπλοκότητας, ανάγνωση πολυπλοκότητας, και αποθήκευσης από πάνω. Δεν υπάρχει δωρεάν γεύμα - είστε πάντα διαπραγμάτευση ένα κόστος για ένα άλλο. Για την οριστική αναφορά σε αυτό το θέμα, δείτε Joe Celko apos? S Δέντρα και Ιεραρχίες στο SQL για Smarties, η οποία καλύπτει όλες αυτές τις προσεγγίσεις σε βάθος.

Το Παράδειγμα: Κρεμασμένα Σχόλια

Για να κάνουμε τις συγκρίσεις συγκεκριμένες, θα χρησιμοποιήσουμε κοχλιωτά σχόλια ως παράδειγμα λειτουργίας μας - κάτι που αυτό το πολύ blog χρησιμοποιεί. Ένα νήμα σχολίων μπορεί να μοιάζει με αυτό:

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

Πρέπει να υποστηρίξουμε αυτές τις επιχειρήσεις:

  • Λάβετε όλα τα σχόλια για μια θέση (με τη δομή κλωστή διατηρηθεί)
  • Πάρτε όλους τους προγόνους. ενός σχολίου (πορεία breadcrumb)
  • Πάρτε όλους τους απογόνους. ενός σχολίου (έντονο υποδέντρο)
  • Προσθήκη νέου σχολίου (ως απάντηση σε υφιστάμενο σχόλιο)
  • Διαγραφή ενός υποδέντρου (Αφαιρέστε ένα σχόλιο και όλες τις απαντήσεις)
  • Μετακίνηση ενός υποδέντρου (προφανές ένα σχόλιο - πιο σπάνιο, αλλά μερικές φορές χρειάζεται)

Οι Πέντε Προσεγγίσεις

Κάθε προσέγγιση καλύπτεται λεπτομερώς στο δικό της άρθρο. Εδώ είναι μια περίληψη για να σας βοηθήσει να επιλέξετε:


1. Κατάλογος adjacency (Παραπομπές αναφοράς)

Διαβάστε ολόκληρο το άρθρο

Η απλούστερη και πιο διαισθητική προσέγγιση. Κάθε σειρά αποθηκεύει μια αναφορά στο γονικό κόμβο του. Είναι αυτό που οι περισσότεροι προγραμματιστές φτάνουν για την πρώτη, επειδή χαρίζει φυσικά στο πώς σκεφτόμαστε για ιεραρχίες.

Πώς λειτουργεί: Κάθε σχόλιο έχει ένα ακυρώσιμο ParentCommentId στήλη. Τα σχόλια ρίζας έχουν NULL, οι απαντήσεις δείχνουν το γονέα τους.

Καλύτερα για: Ρηκτικές ιεραρχίες (λιγότερο από 5-6 επίπεδα), συχνές κινήσεις υποδέντρων, όταν θέλετε οι ιδιότητες πλοήγησης του EF Core να λειτουργούν φυσικά.

Ανταλλαγές: Το να πάρεις προγόνους ή απόγονους απαιτεί αναδρομικά CTEs ή πολλαπλά ερωτήματα.


2. Πίνακας κλεισίματος

Διαβάστε ολόκληρο το άρθρο

Προκατασκευάζει και αποθηκεύει κάθε σχέση προγόνων-απόγονων σε ένα ξεχωριστό τραπέζι. Αντί να υπολογίζει τις σχέσεις κατά την ώρα των ερωτήσεων, τις αποθηκεύουμε ρητά με το βάθος τους.

Πώς λειτουργεί: Ένα ξεχωριστό CommentClosure Σχόλιο 4 στο Σχόλιο 1 μέσω Σχόλιο 3 θα έχουν καταχωρήσεις: (1,4,2), (3,41), (4,40).

Καλύτερα για: Read-βαριές εφαρμογές, όταν χρειάζεται να ρωτήσετε σε αυθαίρετα βάθη, όταν σπάνια μετακινείτε υποδέντρα.

Ανταλλαγές: Πιο περίπλοκα ένθετα (πρέπει να προσθέσετε καταχωρήσεις κλεισίματος), η αποθήκευση μεγαλώνει με βάθος, κινείται subtrees απαιτεί την ανοικοδόμηση του κλεισίματος.


3. Υλοποιημένη διαδρομή

Διαβάστε ολόκληρο το άρθρο

Αποθηκεύει την πλήρη καταγωγή ως μια περιωρισμένη χορδή. Σκεφτείτε το ως αποθήκευση της πλήρους ταχυδρομικής διεύθυνσης και όχι μόνο το όνομα του δρόμου.

Πώς λειτουργεί: Κάθε σχόλιο έχει ένα Path στήλη όπως "1/3/7" που σημαίνει "ρίζα είναι 1, γονιός είναι 3, αυτό είναι 7".

Καλύτερα για: Η γενιά των πρόγονων ρωτιέται περισσότερο από τους απογόνους, όταν θέλεις μονοπάτια που είναι ευανάγνωστα από τον άνθρωπο για αποσφαλμάτωση.

Ανταλλαγές: ΟΠΩΣ τα ερωτήματα μπορούν να είναι αργά χωρίς τα κατάλληλα ευρετήρια, οι συμβολοσειρές μονοπατιών έχουν όρια μήκους, η μετακίνηση υποδέντρων απαιτεί ενημέρωση όλων των απόγονων μονοπατιών.


4. Ενδιαφερόμενα σύνολα

Διαβάστε ολόκληρο το άρθρο

Όλοι οι απόγονοι έχουν αξίες. μεταξύ Τα όρια των γονιών.

Πώς λειτουργεί: Κάθε σχόλιο έχει Left και Right τιμές. Ένας κόμβος με L=4, R=7 περιέχει όλους τους κόμβους όπου 4 < Αριστερά και Δεξιά < 7.

Καλύτερα για: Read-βαρύ, γράψτε-σπάνια σενάρια όπως τα δέντρα κατηγορίας. Εξαιρετικό για "πάρτε ολόκληρο subtree σε σειρά προβολής" ερωτήματα.

Ανταλλαγές: Εισαγωγή απαιτούν ενημέρωση πολλές σειρές (αλλάξτε όλες τις τιμές για να κάνει δωμάτιο), κίνηση subtrees είναι περίπλοκη. Δεν είναι κατάλληλο για συχνές γραφές.


5. PostgreSQL ltree

Διαβάστε ολόκληρο το άρθρο

Η μητρική επέκταση του PostgreSQL για ιεραρχικά δεδομένα. Όπως υλοποιήθηκαν μονοπάτια αλλά με βελτιστοποίηση επιπέδου βάσης δεδομένων και ισχυρό μοτίβο ταιριάζουν.

Πώς λειτουργεί: Χρησιμοποιεί το ltree Οι δείκτες GIST επιτρέπουν την αποτελεσματική πρόγονο / απόγονο ερωτήματα χρησιμοποιώντας τους χειριστές, όπως @> και <@.

Καλύτερα για: PostgreSQL-μόνο ανάπτυξη όπου η απόδοση έχει σημασία, όταν χρειάζεστε μοτίβο ταιριάζουν ερωτήματα, όταν θέλετε το καλύτερο των υλοποιημένων μονοπατιών.

Ανταλλαγές: PostgreSQL-μόνο, προσθέτει εξάρτηση επέκτασης βάσης δεδομένων. Σημείωση: Το Ο πάροχος Npgsql υποστηρίζει μεταφράσεις LINQ για ltree μέσω του LTree τύπος, αν και επαναλαμβανόμενες CTEs εξακολουθούν να απαιτούν ωμό SQL.


Περίληψη σύγκρισης

Πλησιάστε, εισάγετε την βασική υποστήριξη EF EF Core Support |----------|--------|---------------|-----------------|--------------|---------|-----------------| | Κατάλογος adjacency Με CTE O(d) με CTE O(1) με CTE O(1) | Πίνακας κλεισίματος Ο (δ) Ο (Ι) Ο (Ι) Ο (Ι) Ο (σ x δ) Ο (ν x δ) Ωραίος (Ι) Ωραίος (Ι) | Υλοποιημένη διαδρομή O (1) O (1) O (1)* O(1) O(s) O(s) O(d) per node | Φωτεινά σύνολα Οοαααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααάαααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααα | ltreeCity name (optional, probably does not need a translation) Οοοοαααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααααα LTree τύπος)

n = συνολικός κόμβος, d = βάθος, s = μέγεθος υποδέντρου *Με τον κατάλληλο δείκτη

Χαρτιά ροής απόφασης

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: Αυτό το blog

Αυτό το blog χρησιμοποιεί ένα Πίνακας κλεισίματος προσέγγιση για σχόλια. Η λογική:

  1. Τα σχόλια διαβάζονται πολύ πιο συχνά από ό, τι γράφτηκε
  2. Πρέπει να απεικονίσουμε αποτελεσματικά τις ένοχες κλωστές σχολίων.
  3. Τα ερωτήματα που περιορίζουν το βάθος είναι σημαντικά για την απόδοση (θα καλύψουμε σε 5 επίπεδα βαθιά)
  4. Κινούμενα σχόλια είναι σπάνια (φορείς περιστασιακά πρέπει να το κάνουν αυτό)

Βλέπεις; Μέρος 1.2: Πίνακας κλεισίματος για τα πλήρη στοιχεία εφαρμογής.

Πλοήγηση σειράς

logo

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