was ist die effizienteste / eleganteste art, einen flachen tisch in einen baum zu stecken?
<p>angenommen, sie haben eine flache tabelle, in der eine geordnete baumhierarchie gespeichert ist:</p><pre><code>id name parentid order 1 'node 1' 0 10 2 'node 1.1' 1 10 3 'node 2' 0 20 4 'node 1.1.1' 2 10 5 'node 2.1' 3 10 6 'node 1.2' 1 20 </code></pre><p>hier ist ein diagramm, in dem wir <code>[id] name</code> haben. wurzelknoten 0 ist fiktiv.</p><pre> [0] root /\ [1] node 1 [3] node 2 /\\ [2] node 1.1 [6] node 1.2 [5] node 2.1 / [4] node 1.1.1 </pre><p>welchen minimalistischen ansatz würden sie verwenden, um das in html (oder text) als korrekt geordnete, korrekt eingerückte struktur auszugeben?</p><p>angenommen, sie haben nur grundlegende datenstrukturen (arrays und hashmaps), keine ausgefallenen objekte mit parent/child-referenzen, kein orm, kein framework, nur ihre beiden hände. die tabelle wird als ergebnismenge dargestellt, auf die nach dem zufallsprinzip zugegriffen werden kann.</p><p>pseudocode oder einfaches englisch ist in ordnung, dies ist nur eine konzeptionelle frage.</p><p>bonusfrage: gibt es eine grundsätzlich bessere möglichkeit, eine solche baumstruktur in einem rdbms zu speichern?</p><hr><p><strong> änderungen und ergänzungen </strong></p><p>zur beantwortung der frage eines kommentars ( <a href="https://stackoverflow.com/users/17826/mark-bessey">mark bessey</a> ): ein wurzelknoten ist nicht erforderlich, da er sowieso nie angezeigt wird. parentid = 0 ist die konvention, um auszudrücken, dass dies die oberste ebene ist. in der spalte reihenfolge wird festgelegt, wie knoten mit demselben übergeordneten knoten sortiert werden.</p><p>die "ergebnismenge", von der ich gesprochen habe, kann als eine reihe von hashmaps dargestellt werden (um in dieser terminologie zu bleiben). denn mein beispiel sollte schon da sein. einige antworten gehen die extrameile und konstruieren es zuerst, aber das ist okay.</p><p>der baum kann beliebig tief sein. jeder knoten kann n kinder haben. ich hatte jedoch nicht genau einen baum mit "millionen einträgen" im sinn.</p><p>verwechseln sie meine wahl des knotennamens ('node 1.1.1') nicht mit etwas, auf das sie sich verlassen können. die knoten könnten genauso gut 'frank' oder 'bob' genannt werden, es wird keine namensstruktur impliziert, dies dient lediglich dazu, es lesbar zu machen.</p><p><em> ich habe meine eigene lösung veröffentlicht, damit ihr sie in stücke reißen könnt.</em></p>
<p>jetzt, da mysql 8.0 kurz vor der veröffentlichung steht, <a href="https://stackoverflow.com/questions/324935/mysql-with-clause/325243#325243">alle gängigen sql-datenbanken unterstützen rekursive abfragen</a> in der standardsyntax.</p><pre><code>with recursive mytree as ( select * from mytable where parentid is null union all select m.* from mytable as m join mytree as t on m.parentid = t.id ) select * from mytree; </code></pre><p>ich habe rekursive abfragen in mysql 8.0 in meiner präsentation getestet <a href="https://www.slideshare.net/billkarwin/recursive-query-throwdown">recursive query throwdown</a> in 2017.</p><p>unten ist meine ursprüngliche antwort von 2008:</p><hr><p>es gibt verschiedene möglichkeiten, baumstrukturierte daten in einer relationalen datenbank zu speichern. was sie in ihrem beispiel zeigen, verwendet zwei methoden:</p><ul> <li><strong> adjacency list </strong> (die "eltern" -spalte) und</li> <li><strong> pfadaufzählung </strong> (die gepunkteten zahlen in ihrer namensspalte).</li> </ul><p>eine andere lösung heißt <strong> verschachtelte mengen </strong> und kann auch in derselben tabelle gespeichert werden. lesen sie " <a href="https://rads.stackoverflow.com/amzn/click/com/1558609202">bäume und hierarchien in sql für smarties</a> " von joe celko, um weitere informationen zu diesen designs zu erhalten.</p><p>normalerweise bevorzuge ich ein design namens <strong> closure table </strong> (aka "adjacency relation") zum speichern von daten mit baumstruktur. es erfordert eine andere tabelle, aber dann ist das abfragen von bäumen ziemlich einfach.</p><p>ich beschreibe closure table in meiner präsentation <a href="http://www.slideshare.net/billkarwin/models-for-hierarchical-data">modelle für hierarchische daten mit sql und php</a> und in meinem buch <a href="http://pragprog.com/book/bksqla/sql-antipatterns">sql-antimuster: vermeidung der fallstricke der datenbankprogrammierung</a> .</p><pre><code>create table closuretable ( ancestor\_id int not null references flattable(id), descendant\_id int not null references flattable(id), primary key (ancestor\_id, descendant\_id) ); </code></pre><p>speichern sie alle pfade in der closure-tabelle, in der es eine direkte abstammung von einem knoten zum anderen gibt. fügen sie für jeden knoten eine zeile ein, die auf sich selbst verweist. verwenden sie beispielsweise den datensatz, den sie in ihrer frage angezeigt haben:</p><pre><code>insert into closuretable (ancestor\_id, descendant\_id) values (1,1), (1,2), (1,4), (1,6), (2,2), (2,4), (3,3), (3,5), (4,4), (5,5), (6,6); </code></pre><p>jetzt können sie einen baum ab knoten 1 wie folgt erstellen:</p><pre><code>select f.* from flattable f join closuretable a on (f.id = a.descendant\_id) where a.ancestor\_id = 1; </code></pre><p>die ausgabe (im mysql-client) sieht folgendermaßen aus:</p><pre><code>++ | id | ++ | 1 | | 2 | | 4 | | 6 | ++ </code></pre><p>mit anderen worten, die knoten 3 und 5 werden ausgeschlossen, da sie teil einer separaten hierarchie sind und nicht von knoten 1 abstammen.</p><hr><p>betreff: kommentar von e-satis über unmittelbare kinder (oder unmittelbare eltern). sie können eine spalte "<code>path\_length</code>" zu <code>closuretable</code> hinzufügen, um die abfrage nach einem unmittelbaren kind oder elternteil (oder einer anderen entfernung) zu vereinfachen.</p><pre><code>insert into closuretable (ancestor\_id, descendant\_id, path\_length) values (1,1,0), (1,2,1), (1,4,2), (1,6,1), (2,2,0), (2,4,1), (3,3,0), (3,5,1), (4,4,0), (5,5,0), (6,6,0); </code></pre><p>anschließend können sie ihrer suche einen begriff hinzufügen, um die unmittelbaren untergeordneten elemente eines bestimmten knotens abzufragen. dies sind nachkommen, deren <code>path\_length</code> 1 ist.</p><pre><code>select f.* from flattable f join closuretable a on (f.id = a.descendant\_id) where a.ancestor\_id = 1 and path\_length = 1; ++ | id | ++ | 2 | | 6 | ++ </code></pre><hr><p>kommentar von @ashraf: "wie wäre es, den ganzen baum [nach namen] zu sortieren?"</p><p>hier ist eine beispielabfrage, um alle knoten zurückzugeben, die nachkommen von knoten 1 sind, sie mit der flattable zu verbinden, die andere knotenattribute wie <code>name</code> enthält, und nach dem namen zu sortieren.</p><pre><code>select f.name from flattable f join closuretable a on (f.id = a.descendant\_id) where a.ancestor\_id = 1 order by f.name; </code></pre><hr><p>kommentar von @nate:</p><pre><code>select f.name, group\_concat(b.ancestor\_id order by b.path\_length desc) as breadcrumbs from flattable f join closuretable a on (f.id = a.descendant\_id) join closuretable b on (b.descendant\_id = a.descendant\_id) where a.ancestor\_id = 1 group by a.descendant\_id order by f.name +++ | name | breadcrumbs | +++ | node 1 | 1 | | node 1.1 | 1,2 | | node 1.1.1 | 1,2,4 | | node 1.2 | 1,6 | +++ </code></pre><hr><p>ein benutzer hat heute eine änderung vorgeschlagen. so moderatoren haben der bearbeitung zugestimmt, aber ich kehre sie um.</p><p>die bearbeitung schlug vor, dass order by in der letzten obigen abfrage <code>order by b.path\_length, f.name</code> lauten sollte, vermutlich um sicherzustellen, dass die reihenfolge mit der hierarchie übereinstimmt. dies funktioniert jedoch nicht, da "node 1.1.1" nach "node 1.2" bestellt wird.</p><p>wenn sie möchten, dass die reihenfolge der hierarchie auf sinnvolle weise entspricht, ist dies möglich, jedoch nicht einfach durch sortieren nach der pfadlänge. siehe zum beispiel meine antwort auf <a href="https://stackoverflow.com/questions/8252323/mysql-closure-table-hierarchical-database-how-to-pull-information-out-in-the-c">hierarchische mysql closure table-datenbank - informationen in der richtigen reihenfolge abrufen</a> .</p>
Es lohnt sich, einen Blick auf diesen [`Blog Post`](http://lostechies.com/jimmybogard/2012/07/20/choosing-an-orm-strategy/) werfen, aber letztendlich werden die Kriterien von Ihnen festgelegt. Sie müssen entscheiden, ob 

* schnelle Entwicklung
* Performance
* Code-zuerst
* DB zuerst

 oder was auch immer Sie suchen und priorisieren Sie die Liste basierend auf, was noch wichtiger ist. Dann verwenden Sie diese Liste, um Ihre Forschung zusammenzustellen. <p>dies kann eine paginierung sein oder auch nicht, aber hier ist, wie ich paginierung in meiner web-app mache:</p><pre><code>|&lt; first, &lt;&lt; previous, page [5] of 12, next &gt;&gt;, last &gt;| </code></pre><p>die äußeren vier optionen sind links, während die <code>page [5] of 12</code> ist ein formular mit dem [5] als eingabefeld. für benutzer mit aktiviertem javascript verstecke ich die schaltfläche "senden" und sende das formular beim ereignis "unfokus" oder drücke den tastendruck. der benutzer hat die option, wie viele zeilen er anzeigen möchte, die er aus einem dropdown-menü auswählen kann: 10, 20, 50, 100, alle. zusätzlich zur paginierung biete ich auch das sortieren und filtern der ergebnisse an, obwohl dies möglicherweise nicht in jeder situation sinnvoll ist.</p><p><strong>bearbeiten</strong>:</p><p>hier ist ein ausschnitt aus einem modell unserer web-app, um ihnen eine bessere vorstellung zu geben. in dieser version wurden die wörter in den schaltflächen weggelassen, werden jedoch nach einer leichten verzögerung als tooltip beim hover angezeigt. möglicherweise stellen sie jedoch fest, dass das beibehalten der wörter zu einer höheren benutzerfreundlichkeit führt.</p><p><img src="https://i.stack.imgur.com/vzbu0.png" alt="virtuosi media&#39;s pagination example"></p><p>ein paar andere dinge, auf die sie hinweisen sollten:</p><ul> <li>die schaltflächen "erste" und "vorherige" wurden deaktiviert, da es sich um die erste seite handelt.</li> <li>das blau zeigt aktive schaltflächen an (ich muss dieses spezielle thema durch eine farbprüfung ausführen, um sicherzustellen, dass farbenblinde benutzer einen unterschied feststellen können).</li> <li>die tasten bleiben immer erhalten, auch wenn sie deaktiviert sind. dies gibt der schnittstelle konsistenz.</li> <li>aufgrund der skalierung habe ich ein eingabefeld anstelle eines dropdown-auswahlfelds ausgewählt. wenn sie 3000 seiten haben, möchten sie nicht nach unten scrollen, um eine seite zu finden</li> <li>die paginierung ist in einer singulären visuellen komponente gruppiert, um ähnlichkeit im zweck anzuzeigen.</li> </ul> <p>wenn dies meine entwurfsentscheidung wäre, würde ich wahrscheinlich eher eine option c (modifizierte option a) wählen.</p><h3>erstens, warum nicht 'option b':</h3><p>zum einen gefällt mir die klarheit, dass jedes produkt seinen eigenen tisch hat. wenn es sich nur um eine große tabelle mit einem feld zur bestimmung des typs handelt, ist die beziehung nicht so klar.</p><p>zum anderen würde die indizierungsstrategie immer erfordern, dass dieses typfeld aufgelistet wird. da es sich nur um 4 typen handelt, ist die indexkardinalität extrem niedrig (<code>select * from product\_table where type='x'</code> führt im grunde sowieso einen vollständigen tabellenscan durch)</p><h3>option c.</h3><ul> <li>erstellen sie eine übergeordnete tabelle, die nur die spalten enthält, die alle typen gemeinsam nutzen</li> <li>erstellen sie jeden produkttyp als eigene tabelle mit ihren einzelnen spalten, mit einem zusätzlichen: ein link zur übergeordneten tabelle</li> <li>erstellen sie jede 'link'-tabelle: product\_option, model\_option usw. mit links zu den jeweiligen schlüsseln.</li> <li>für diejenigen mit wechselseitigen links (model\_option, option\_model) erstellen sie auch diese tabellen. dies erhöht die klarheit ihrer joins für alle, die sich das ansehen.</li> </ul><p>der nachteil ist die komplexität, sicherzustellen, dass waisen vermieden werden, wenn dinge aktualisiert/gelöscht werden, und zunächst die abfragen zu entwerfen, die diese tabellen verwenden.</p> plastikflasche zur hälfte mit einer mischung aus süßen säften mischen und an einem baum aufhängen, den deckel oben weglassen. kannst auch an der seite ein paar kleine löcher reinmachen... dann können mehr rein. die wespen fliegen rein und genießen den saft und sind somit auch shcon gefangen, weil sie nichtmehr rauskommen. sie können nicht mehr raus, weil wespen nicht in der lage sind vertikal zu fliegen. sie kommen zwar nach oben, sehen aber nciht, wo der ausgang ist und sitzen fest. man kann auch mehrere solcher flaschen aufhängen, dann gehts schneller ;) <p>ja, es ist einfach eine wiederkehrende phrase im titel mehrerer artikel, beginnend mit einem paar in den 70er jahren, in dem sussman und steele die verwendung der lambda-rechnung für die programmierung anhand eines minimalistischen lisp-dialekts mit dem namen " <a href="http://en.wikipedia.org/wiki/scheme\_%28programming\_language%29">schema" demonstrieren</a> "sie haben für diesen zweck entwickelt. sie finden <a href="http://library.readscheme.org/page1.html">die papiere selbst hier</a> ; sie sind interessant und überraschend relevant.</p><p>ich bin mir nicht sicher, ob dies jemals explizit angegeben wird, aber es ist klar (aus dem kontext, nachdem ich die artikel gelesen habe und den allgemeinen hintergrund und die forschungsinteressen der autoren kenne), dass der satz einfach ein eingängiger slogan für ihre behauptung ist, dass lambda-abstraktionen sind als rechnerisches grundelement nicht nur universell im formalen sinne (in der lage zu sein, ein programm auf irgendeine weise zu codieren, wie umständlich es auch sein mag), sondern universell in einem <em> praktischen </em> spüren, dass jedes in anderen sprachen vorhandene konstrukt, auch solche, die von grund auf eingebrannt wurden, in einer sprache auf lambda-basis auf eine weise implementiert werden kann, die sowohl effektiv als auch natürlich zu verwenden ist.</p><p>der wiederholte satz führt zu der offensichtlichen verallgemeinerten form "für alle x ist lambda das ultimative x", was der sinn ist, den ich allgemein als "lambda the ultimate" als blognamen verstanden habe, wobei ich feststelle, dass sich ltu mit der programmiersprache befasst design und theorie. ironischerweise wäre ltu wahrscheinlich auch einer der besten orte, um jemanden zu finden, der ihnen etwas erzählen könnte, für das lambda <em> nicht </em> die ultimative implementierung ist. :]</p><p>beachten sie auch, dass sussman einer der autoren von <a href="http://mitpress.mit.edu/sicp/"> sicp </a> ist, einem sehr einflussreichen lehrbuch, das auch die schemasprache verwendet und viel zeit damit verbringt, lambda-abstraktionen als einzuführen ein konzept.</p> Hier eine Skizze, damit du es dir vorstellen kannst

![blob.png](https://www.mathelounge.de/?qa=blob&qa_blobid=4970608801118703028) Aloha :)


Ich hoffe, dass ich den Aufbau der Determinante richtig verstanden habe. Um die Idee zu verdeutlichen, schreibe ich die ersten 4 Fälle einfach mal auf:


Bei \(n=1\) ist nicht viel zu tun:$$D\_1=\left|\begin{array}{c}1\end{array}\right|=1$$


Bei \(n=2\) addiere ich Zeile 1 zu Zeile 2 und ziehe danach aus Zeile 2 den Faktor vor die Determinante:$$D\_2=\left|\begin{array}{c}1 & 1\\-1 & 1\end{array}\right|=\left|\begin{array}{c}1 & 1\\0 & 2\end{array}\right|=2\cdot\left|\begin{array}{c}1 & 1\\0 & 1\end{array}\right|=2$$
  



Bei \(n=3\) addiere ich Zeile 1 zu Zeile 2 und ziehe danach aus Zeile 2 den Faktor vor die Determinante:$$D\_3=\left|\begin{array}{c}1 & 1 & 0\\-1 & 1 & 4\\0 & -1 & 1\end{array}\right|=\left|\begin{array}{c}1 & 1 & 0\\0 & 2 & 4\\0 & -1 & 1\end{array}\right|=2\cdot\left|\begin{array}{c}1 & 1 & 0\\0 & 1 & 2\\0 & -1 & 1\end{array}\right|$$Jetzt addiere ich weiter Zeile 2 zu Zeile 3 und ziehe aus Zeile 3 den Faktor vor:$$\phantom{D\_3}=2\cdot\left|\begin{array}{c}1 & 1 & 0\\0 & 1 & 2\\0 & 0 & 3\end{array}\right|=2\cdot3\cdot\left|\begin{array}{c}1 & 1 & 0\\0 & 1 & 2\\0 & 0 & 1\end{array}\right|=6$$


Bei \(n=4\) wiederhole ich zunächst alle Schritte vom Fall \(n=3\):


$$D\_4=\left|\begin{array}{c}1 & 1 & 0 & 0\\-1 & 1 & 4 & 0\\0 & -1 & 1 & 9\\0 & 0 & -1 & 1\end{array}\right|=\left|\begin{array}{c}1 & 1 & 0 & 0\\0 & 2 & 4 & 0\\0 & -1 & 1 & 9\\0 & 0 & -1 & 1\end{array}\right|=2\cdot\left|\begin{array}{c}1 & 1 & 0 & 0\\0 & 1 & 2 & 0\\0 & -1 & 1 & 9\\0 & 0 & -1 & 1\end{array}\right|$$$$\phantom{D\_4}=2\cdot\left|\begin{array}{c}1 & 1 & 0 & 0\\0 & 1 & 2 & 0\\0 & 0 & 3 & 9\\0 & 0 & -1 & 1\end{array}\right|=2\cdot3\cdot\left|\begin{array}{c}\underline1 & \underline1 & \underline0 & 0\\\underline0 & \underline1 & \underline2 & 0\\\underline0 & \underline0 & \underline1 & 3\\0 & 0 & -1 & 1\end{array}\right|$$Man erkennt die letzte Determinante aus der Berechnung von \(D\_3\) wieder (unterstrichene Einträge), nur sind jetzt noch die 3 Elemente in der rechten unteren Ecke dazu gekommen, alle anderen zusätzlichen Einträge sind 0. Wir können daher noch einen weiteren Schritt (Addition der vorletzten Zeile zur letzten und Faktor herausziehen) durchführen:$$\phantom{D\_4}=2\cdot3\cdot\left|\begin{array}{c}1 & 1 & 0 & 0\\0 & 1 & 2 & 0\\0 & 0 & 1 & 3\\0 & 0 & 0 & 4\end{array}\right|=2\cdot3\cdot4\cdot\left|\begin{array}{c}1 & 1 & 0 & 0\\0 & 1 & 2 & 0\\0 & 0 & 1 & 3\\0 & 0 & 0 & 1\end{array}\right|=24$$


Damit ist klar, dass: \(D\_n=n!\)


Dieses Vorgehen kannst du, wenn verlangt, noch in einen sauberen Induktionsbeweis packen. Die Beweisidee sollte zumindest jetzt klar sein ;) <h2>aufgrund der empfehlungen in den kommentaren habe ich begonnen, <a href="http://tools.tortoisesvn.net/grepwin.html">grepwin</a> zu verwenden, und es ist fantastisch und <em> kostenlos</em>.</h2><hr><p>(ich bin immer noch ein fan von <a href="http://www.powergrep.com/">powergrep</a> , aber ich benutze es nicht mehr.)</p><p>ich weiß, dass sie es bereits erwähnt haben, aber powergrep ist <strong> fantastisch </strong>.</p><p>einige meiner lieblingsfunktionen sind:</p><ul> <li>klicken sie mit der rechten maustaste auf einen ordner, um powergrep darauf auszuführen</li> <li>verwenden sie reguläre ausdrücke oder wörtlichen text</li> <li>geben sie platzhalter für dateien an, die ein- und ausgeschlossen werden sollen</li> <li>suchen &amp; ersetzen</li> <li>der vorschaumodus ist gut, da sie sicherstellen können, dass sie das ersetzen, was sie beabsichtigen.</li> </ul><p>jetzt ist mir klar, dass die anderen grep-tools all das tun können. es ist nur so, dass powergrep alle funktionen in einer sehr benutzerfreundlichen benutzeroberfläche zusammenfasst.</p><p><strong> <em> von den gleichen wundervollen leuten, die sie mitgebracht haben <a href="http://en.wikipedia.org/wiki/regexbuddy">regexbuddy</a> und denen ich nichts mehr zu tun habe, als ihre sachen zu lieben.</em> </strong> (es sollte beachtet werden, dass regexbuddy eine basisversion von grep (für windows) selbst enthält und viel weniger kostet als powergrep.)</p><hr><h2>zusätzliche lösungen</h2><h3>vorhandene windows-befehle</h3><ul> <li> <a href="http://ss64.com/nt/findstr.html"> findstr </a> </li> <li> <a href="https://technet.microsoft.com/en-us/library/hh849903.aspx">auswahlzeichenfolge</a> in powershell</li> </ul><h3>linux-befehlsimplementierungen unter windows</h3><ul> <li> <a href="http://en.wikipedia.org/wiki/cygwin">cygwin</a> </li> <li> <a href="https://github.com/dthree/cash">bargeld</a> </li> </ul><h3>grep-tools mit grafischer oberfläche</h3><ul> <li> <a href="http://astrogrep.sourceforge.net/screenshots/">astrogrep</a> </li> <li> <a href="https://www.baremetalsoft.com/baregrep/">baregrep</a> </li> <li> <a href="http://stefanstools.sourceforge.net/grepwin.html">grepwin</a> </li> </ul><h3>zusätzliche grep-tools</h3><ul> <li> <a href="http://code.google.com/p/dngrep/">dngrep</a> </li> </ul> Kein kurzer Schnitt, sondern kapselt dies in eine Funktion ein, und Ihr Code ist viel lesbarer. <p>es gibt optionen, die ihnen dabei helfen:</p><pre><code> --insert-ignore insert rows with insert ignore. --replace use replace into instead of insert into. -t, --no-create-info don't write table creation info. </code></pre><p>denken sie an dieses paradigma</p><ul> <li>mysqldump alles von db1 in dump1</li> <li>laden sie dump1 in db3</li> <li>mysqldump alles aus db2 mit --replace (oder --insert-ignore) und --no-create-info in dump2</li> <li>laden sie dump2 in db3</li> </ul>