Ich muss einige Testdaten erstellen, die eine Hierarchie beinhalten. Ich könnte es einfach machen und ein paar CROSS JOIN
s machen, aber das würde mir eine Struktur geben, die völlig einheitlich / ohne jede Variation ist. Das wirkt nicht nur langweilig, sondern die fehlende Variation der Testdaten maskiert manchmal Probleme, die sonst auftreten würden. Daher möchte ich eine ungleichmäßige Hierarchie generieren, die diesen Regeln folgt:
- 3 Ebenen tief
- Level 1 besteht zufällig aus 5 - 20 Knoten
- Stufe 2 besteht aus 1 bis 10 Knoten, die für jeden Knoten der Stufe 1 zufällig ausgewählt werden
- Stufe 3 besteht aus 1 bis 5 Knoten, die für jeden Knoten der Stufe 2 zufällig ausgewählt werden
- Alle Zweige werden 3 Ebenen tief sein. Gleichmäßigkeit in der Tiefe ist an dieser Stelle in Ordnung.
- Die Namen der untergeordneten Knoten auf einer bestimmten Ebene können sich überschneiden (dh die Namen der untergeordneten Knoten müssen nicht für alle Knoten auf derselben Ebene eindeutig sein).
- Der Begriff "zufällig" wird hier als pseudozufällig, nicht eindeutig zufällig definiert. Dies muss erwähnt werden, da der Begriff "zufällig" häufig verwendet wird, um "zufällige Reihenfolge eines gegebenen Satzes, der keine Duplikate erzeugt" zu bedeuten. Ich akzeptiere, dass random = random ist und wenn die Anzahl der Kinder pro Knoten der Ebene 1 nur 4, 7 und 8 beträgt, sogar auf 20 Knoten der Ebene 1, die eine potenzielle Streuung von 1 bis 10 Kindern pro Knoten aufweisen. dann ist das in ordnung, denn das ist was zufällig ist.
- Obwohl dies mit verschachtelten
WHILE
Schleifen recht einfach möglich ist, ist es vorzuziehen, einen satzbasierten Ansatz zu finden. Generell hat das Generieren von Testdaten nicht die Anforderungen an die Effizienz, die der Produktionscode stellen würde, aber das Filmen nach einem satzbasierten Ansatz ist wahrscheinlich lehrreicher und hilft in Zukunft, satzbasierte Ansätze für Probleme zu finden. SoWHILE
Schleifen sind nicht ausgeschlossen-out, kann aber nur verwendet werden , wenn kein Satz-basierten Ansatz möglich ist. - Set-basiert = idealerweise eine einzelne Abfrage, unabhängig von CTEs, APPLYs usw. Die Verwendung einer vorhandenen oder Inline-Nummerntabelle ist also in Ordnung. Die Verwendung eines WHILE / CURSOR / prozeduralen Ansatzes funktioniert nicht. Ich nehme an, Teile der Daten in temporären Tabellen oder Tabellenvariablen abzulegen, nur solange die Operationen alle satzbasiert sind, keine Schleifen. Allerdings wird ein Ansatz mit nur einer Abfrage wahrscheinlich mehreren Abfragen vorgezogen, es sei denn, es kann gezeigt werden, dass der Ansatz mit mehreren Abfragen tatsächlich besser ist. Bitte beachten Sie auch, dass das, was "besser" ist, normalerweise subjektiv ist ;-). Bitte beachten Sie auch, dass die Verwendung von "typisch" im vorherigen Satz ebenfalls subjektiv ist.
- Jede Version und Edition von SQL Server (2005 und neuer, nehme ich an) reicht aus.
- Nur reines T-SQL: nichts von dem albernen SQLCLR-Zeug !! Zumindest in Bezug auf die Generierung der Daten. Das Erstellen der Verzeichnisse und Dateien erfolgt mit SQLCLR. Aber hier konzentriere ich mich nur darauf, die Werte dessen zu erzeugen, was geschaffen werden soll.
- T-SQL-TVFs mit mehreren Anweisungen werden als prozedural und nicht als satzbasiert betrachtet, obwohl sie äußerlich den prozeduralen Ansatz in einem Satz maskieren. Es gibt Zeiten, in denen das absolut angemessen ist. Dies ist keine dieser Zeiten. In diesem Sinne sind auch T-SQL-Skalarfunktionen nicht zulässig, nicht nur, weil sie auch prozedural sind, sondern das Abfrageoptimierungsprogramm speichert ihren Wert manchmal zwischen und wiederholt ihn, sodass die Ausgabe nicht wie erwartet erfolgt.
- T-SQL-Inline-TVFs (auch als iTVFs bezeichnet) sind okey-dokey, da sie satzbasiert sind und im Grunde das Gleiche wie die Verwendung
[ CROSS | OUTER ] APPLY
, die oben als ok angegeben wurde. - Wiederholte Ausführungen der Abfrage (n) sollten größtenteils andere Ergebnisse als beim vorherigen Durchlauf liefern.
- Erläuterung Update 1: Die endgültige Ergebnismenge sollte so ausgedrückt werden, dass sie eine Zeile für jeden einzelnen Knoten von Level3 enthält, wobei der vollständige Pfad bei Level1 beginnt. Dies bedeutet, dass sich die Werte für Ebene 1 und Ebene 2 zwangsläufig in einer oder mehreren Zeilen wiederholen, es sei denn, es gibt nur einen einzigen Knoten für Ebene 2, der nur einen einzigen Knoten für Ebene 3 enthält.
- Klarstellung Update 2: Es gibt eine sehr starke Präferenz für jeden Knoten, der einen Namen oder eine Bezeichnung hat, und nicht nur eine Nummer. Dadurch werden die resultierenden Testdaten aussagekräftiger und realistischer.
Ich bin mir nicht sicher, ob diese zusätzlichen Informationen von Bedeutung sind, aber für den Fall, dass es hilfreich ist, einen Kontext zu haben, beziehen sich die Testdaten auf meine Antwort auf diese Frage:
Importieren Sie XML-Dateien in SQL Server 2012
Obwohl dies an dieser Stelle nicht relevant ist, besteht das Endziel der Generierung dieser Hierarchie darin, eine Verzeichnisstruktur zum Testen rekursiver Dateisystemmethoden zu erstellen. Die Ebenen 1 und 2 sind Verzeichnisse, und Ebene 3 ist der Dateiname. Ich habe mich umgesehen (sowohl hier als auch über die Googles) und nur einen Hinweis auf die Generierung einer zufälligen Hierarchie gefunden:
Linux: Erstellen Sie eine zufällige Verzeichnis- / Dateihierarchie
Diese Frage (zu StackOverflow) ist in Bezug auf das gewünschte Ergebnis eigentlich ziemlich nah, da damit auch versucht wird, eine Verzeichnisstruktur zum Testen zu erstellen. Aber diese Frage (und die Antworten) konzentrieren sich auf Linux / Unix-Shell-Skripte und nicht so sehr auf die satzbasierte Welt, in der wir leben.
Jetzt weiß ich, wie man zufällige Daten erzeugt, und erstelle dabei den Inhalt der Dateien, damit sie auch Variationen zeigen können. Das Knifflige dabei ist, dass die Anzahl der Elemente in jeder Menge zufällig ist und kein bestimmtes Feld. Und , um die Anzahl der Elemente innerhalb der einzelnen Knoten Bedürfnisse von anderen Knoten auf den gleichen Ebene zufällig sein.
Beispielhierarchie
Level 1
Level 3
|---- A
| |-- 1
| | |--- I
| |
| |-- 2
| |--- III
| |--- VI
| |--- VII
| |--- IX
|
|---- B
| |-- 87
| |--- AAA
| |--- DDD
|
|---- C
|-- ASDF
| |--- 11
| |--- 22
| |--- 33
|
|-- QWERTY
| |--- beft
|
|-- ROYGBP
|--- Poi
|--- Moi
|--- Soy
|--- Joy
|--- Roy
Beispiel für eine Ergebnismenge, die die Hierarchie oben beschreibt
Level 1 Level 2 Level 3
A 1 I
A 2 III
A 2 VI
A 2 VII
A 2 IX
B 87 AAA
B 87 DDD
C ASDF 11
C ASDF 22
C ASDF 33
C QWERTY beft
C ROYGBP Poi
C ROYGBP Moi
C ROYGBP Soy
C ROYGBP Joy
C ROYGBP Roy
quelle
TOP(n)
in den 2CROSS APPLY
s nicht richtig zum Arbeiten bringen . Ich bin mir nicht sicher, was ich anders / falsch gemacht habe, da ich diesen Code losgeworden bin, als etwas anderes funktionierte. Ich werde das bald posten, nachdem Sie dieses Update bereitgestellt haben. Und ich habe die meisten meiner Kommentare oben aufgeräumt.n
Elemente über eine WHERE-Bedingung abgerufen, und 2) ich habe diename
Komponente, die kontrollierter ist als das Randomisieren von Verzeichnis- und / oder Dateinamen .@Elemets
, um für jede Ebene einen anderen Satz von Namen zur Auswahl zu erhalten.Das war interessant.
Mein Ziel war es, eine bestimmte Anzahl von Ebenen mit einer zufälligen Anzahl von untergeordneten Zeilen pro Ebene in einer ordnungsgemäß verknüpften hierarchischen Struktur zu generieren. Sobald diese Struktur fertig ist, ist es einfach, zusätzliche Informationen wie Datei- und Ordnernamen hinzuzufügen.
Deshalb wollte ich eine klassische Tabelle zum Speichern eines Baums generieren:
Da es sich um eine Rekursion handelt, scheint ein rekursiver CTE eine natürliche Wahl zu sein.
Ich brauche eine Zahlentabelle . Die Zahlen in der Tabelle sollen von 1. starten Es mindestens 20 Zahlen in der Tabelle sein soll:
MAX(LvlMax)
.Parameter für die Datengenerierung sollten in einer Tabelle gespeichert werden:
Beachten Sie, dass die Abfrage sehr flexibel ist und alle Parameter an einer Stelle getrennt sind. Sie können bei Bedarf weitere Ebenen hinzufügen, indem Sie einfach eine zusätzliche Reihe von Parametern hinzufügen.
Um eine solche dynamische Generierung zu ermöglichen, musste ich mir die zufällige Anzahl von Zeilen für die nächste Ebene merken, also habe ich eine zusätzliche Spalte
ChildRowCount
.Das Erzeugen von Unikaten
IDs
ist auch etwas knifflig. Ich habe das Limit von 100 untergeordneten Zeilen pro übergeordneter Zeile fest programmiert,IDs
um zu gewährleisten, dass diese Zeilen nicht wiederholt werden. Darum geht esPOWER(100, CTE.Lvl)
hier. Infolgedessen gibt es große LückenIDs
. Diese Zahl könnte eine seinMAX(LvlMax)
, aber ich habe der Einfachheit halber eine Konstante von 100 in die Abfrage eingegeben. Die Anzahl der Ebenen ist nicht fest codiert, sondern wird durch festgelegt@Intervals
.Diese Formel
generiert eine zufällige Gleitkommazahl im Bereich
[0..1)
, die dann auf das erforderliche Intervall skaliert wird.Die Abfragelogik ist einfach. Es ist rekursiv. Der erste Schritt generiert eine Reihe von Zeilen der ersten Ebene. Die Anzahl der Zeilen wird durch die Zufallszahl in bestimmt
TOP
. Außerdem ist für jede Zeile eine separate Zufallszahl von untergeordneten Zeilen in gespeichertChildRowCount
.Rekursiver Teil wird verwendet
CROSS APPLY
, um eine bestimmte Anzahl von untergeordneten Zeilen pro übergeordneter Zeile zu generieren. Ich mussteWHERE Numbers.Number <= CTE.ChildRowCount
stattdessen verwendenTOP(CTE.ChildRowCount)
, weilTOP
es im rekursiven Teil von CTE nicht erlaubt ist. Ich wusste vorher nichts über diese Einschränkung von SQL Server.WHERE CTE.ChildRowCount IS NOT NULL
stoppt die Rekursion.SQL-Geige
Ergebnis (mit etwas Glück kann es bis zu 20 + 20 * 10 + 200 * 5 = 1220 Zeilen geben)
Generieren eines vollständigen Pfads anstelle einer verknüpften Hierarchie
Wenn wir nur an den vollständigen Pfadebenen interessiert sind
N
, können wirID
undParentID
aus dem CTE weglassen . Wenn wir eine Liste möglicher Namen in der Ergänzungstabelle habenNames
, ist es einfach, sie aus dieser Tabelle in CTE auszuwählen. DieNames
Tabelle sollte für jede Ebene genügend Zeilen enthalten: 20 für Ebene 1, 10 für Ebene 2, 5 für Ebene 3; 20 + 10 + 5 = 35 insgesamt. Es ist nicht notwendig, für jede Ebene unterschiedliche Reihen zu erstellen, aber es ist einfach, sie richtig einzurichten, also habe ich es getan.SQL Fiddle Hier ist die letzte Abfrage. Ich teilte das
FullPath
inFilePath
und aufFileName
.Ergebnis
quelle
INNER JOIN
im Finale nur 2 s dauernSELECT
. Können schließlich jedem Knoten Namen / Bezeichnungen zugewiesen werden, damit es sich nicht nur um Zahlen handelt? Ich werde die Frage aktualisieren, um diese beiden Punkte zu klären.FullPath
inFilePath
undFileName
.Also hier ist, was ich mir ausgedacht habe. Mit dem Ziel, eine Verzeichnisstruktur zu erstellen, suchte ich nach verwendbaren "Namen" für die Verzeichnisse und Dateien. Da ich es nicht schaffte, die Abfragen
TOP(n)
in denCROSS APPLY
s zum Laufen zu bringen (ich glaube, ich habe versucht, die Abfragen zu korrelieren, indem ich einen Wert aus dem übergeordneten Element als denn
in dem verwendeten verwendete,TOP(n)
aber dann war es nicht zufällig), entschied ich mich, einen Typ von "Zahlen" zu erstellen. Tabelle, die es einerINNER JOIN
oderWHERE
Bedingung ermöglicht, eine Menge vonn
Elementen zu erzeugen, indem einfach eine Zahl zufällig ausgewählt und als angegeben wirdWHERE table.Level = random_number
. Der Trick ist, dass es nur eine Zeile für Ebene 1, zwei Zeilen für Ebene 2, drei Zeilen für Ebene 3 usw. gibt. Wenn Sie also verwenden,WHERE LevelID = 3
erhalte ich 3 Zeilen, und jede Zeile hat einen Wert, den ich als Verzeichnisnamen verwenden kann.INSTALLIEREN
Dieser Teil wurde ursprünglich als Teil des CTE inline spezifiziert. Aus Gründen der Lesbarkeit (damit Sie nicht durch viele
INSERT
Anweisungen scrollen müssen, um zu den wenigen Zeilen der tatsächlichen Abfrage zu gelangen) habe ich sie in eine lokale temporäre Tabelle aufgeteilt.HAUPTABFRAGE
Für Level 1 habe ich nur
[name]
Werte herausgegriffen,sys.objects
da es dort immer viele Zeilen gibt. Wenn ich jedoch mehr Kontrolle über die Namen benötigte, konnte ich die#Elements
Tabelle einfach erweitern , um zusätzliche Ebenen zu erhalten.ABFRAGE ANGEPASST, UM DEN WEG, DEN NAMEN UND DEN INHALT JEDER DATEI ZU ERZEUGEN
Um die vollständigen Pfade für die Dateien und den Dateiinhalt zu generieren, habe ich das Haupt-SELECT des CTE nur zu einem weiteren CTE gemacht und ein neues Haupt-SELECT hinzugefügt, das die richtigen Ausgaben liefert, die einfach in Dateien gehen müssen.
EXTRA-KREDIT
Obwohl dies nicht Teil der in der Frage angegebenen Anforderungen ist, bestand das erwähnte Ziel darin, Dateien zu erstellen, mit denen rekursive Dateisystemfunktionen getestet werden können. Wie nehmen wir also diese Ergebnismenge von Pfadnamen, Dateinamen und Dateiinhalten und tun etwas damit? Wir brauchen nur zwei SQLCLR-Funktionen: eine zum Erstellen der Ordner und eine zum Erstellen der Dateien.
Um diese Daten funktionsfähig zu machen, habe ich den Hauptteil
SELECT
des CTE wie folgt geändert :quelle