Erstellen Sie eine Hierarchie mit mehreren Ebenen, wobei jeder Knoten eine zufällige Anzahl von untergeordneten Knoten hat

16

Ich muss einige Testdaten erstellen, die eine Hierarchie beinhalten. Ich könnte es einfach machen und ein paar CROSS JOINs 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 WHILESchleifen 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. So WHILESchleifen 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
Solomon Rutzky
quelle

Antworten:

9

( Anmerkung des OP: Bevorzugte Lösung ist der 4. / letzte Codeblock)

XML scheint mir die naheliegende Wahl für die hier zu verwendende Datenstruktur zu sein.

with N as
(
  select T.N
  from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),
              (12),(13),(14),(15),(16),(17),(18),(19),(20)) as T(N)
)

select top(5 + abs(checksum(newid())) % 15)
  N1.N as '@Value',
  (
  select top(1 + abs(checksum(newid())) % 10)
    N2.N as '@Value',
    (
    select top(1 + abs(checksum(newid())) % 5)
      N3.N as '@Value'
    from N as N3
    where N2.N > 0
    for xml path('Level3'), type
    )
  from N as N2
  where N1.N > 0
  for xml path('Level2'), type
  )
from N as N1
for xml path('Level1'), root('Root');

Der Trick, damit SQL Server top()für jeden Knoten andere Werte verwendet, besteht darin, die Unterabfragen zu korrelieren. N1.N > 0und N2.N > 0.

XML reduzieren:

declare @X xml;

with N as
(
  select T.N
  from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),
              (12),(13),(14),(15),(16),(17),(18),(19),(20)) as T(N)
)
select @X  = (
             select top(5 + abs(checksum(newid())) % 15)
               N1.N as '@Value',
               (
               select top(1 + abs(checksum(newid())) % 10)
                 N2.N as '@Value',
                 (
                 select top(1 + abs(checksum(newid())) % 5)
                   N3.N as '@Value'
                 from N as N3
                 where N2.N > 0
                 for xml path('Level3'), type
                 )
               from N as N2
               where N1.N > 0
               for xml path('Level2'), type
               )
             from N as N1
             for xml path('Level1')
             );


select L1.X.value('@Value', 'varchar(10)')+'\'+
       L2.X.value('@Value', 'varchar(10)')+'\'+
       L3.X.value('@Value', 'varchar(10)')
from @X.nodes('/Level1') as L1(X)
  cross apply L1.X.nodes('Level2') as L2(X)
  cross apply L2.X.nodes('Level3') as L3(X);

Und eine Version ohne XML.

with N as
(
  select T.N
  from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),
              (12),(13),(14),(15),(16),(17),(18),(19),(20)) as T(N)
)
select cast(N1.N as varchar(10))+'\'+
       cast(N2.N as varchar(10))+'\'+
       cast(N3.N as varchar(10))
from (
     select top(5 + abs(checksum(newid())) % 15)
       N.N
     from N
     ) as N1
  cross apply
     (
     select top(1 + abs(checksum(newid())) % 10)
       N.N
     from N
     where N1.N > 0
     ) as N2
  cross apply
     (
     select top(1 + abs(checksum(newid())) % 5)
       N.N
     from N
     where N2.N > 0
     ) as N3;

Korrelation N1.N > 0und N2.N > 0ist immer noch wichtig.

Eine Version mit einer Tabelle mit 20 Namen, die anstelle von ganzen Zahlen verwendet werden soll.

declare @Elements table
(
  Name nvarchar(50) not null
);

insert into @Elements(Name)
select top(20) C.name 
from sys.columns as C
group by C.name;

select N1.Name + N'\' + N2.Name + N'\' + N3.Name
from (
     select top(5 + abs(checksum(newid())) % 15)
       E.Name
     from @Elements as E
     ) as N1
  cross apply
     (
     select top(1 + abs(checksum(newid())) % 10)
       E.Name
     from @Elements as E
     where N1.Name > ''
     ) as N2
  cross apply
     (
     select top(1 + abs(checksum(newid())) % 5)
       E.Name
     from @Elements as E
     where N2.Name > ''
     ) as N3;
Mikael Eriksson
quelle
1
Mir gefällt die neue Version besser. Es ist fast dasselbe, was ich mir bei meinem ersten Versuch ausgedacht habe, aber aus irgendeinem Grund konnte ich es TOP(n)in den 2 CROSS APPLYs 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.
Solomon Rutzky
Ich habe gerade meine Version gepostet. Die Hauptunterschiede sind: 1) Da ich TOP (n) nicht zum Laufen bringen konnte, habe ich nElemente über eine WHERE-Bedingung abgerufen, und 2) ich habe die nameKomponente, die kontrollierter ist als das Randomisieren von Verzeichnis- und / oder Dateinamen .
Solomon Rutzky
Es tut mir leid, so lange weg zu sein, aber ich war verrückt beschäftigt. Dennoch habe ich darüber nachgedacht und kann mich nicht zwischen meiner Antwort und Ihrer Nicht-XML-Version entscheiden. Ich mag die Einfachheit und Flexibilität von Ihnen, brauche aber die Fähigkeit, Namen zurückzugeben, um eine Ordnerstruktur zu erstellen, die meine hat. Dann wurde mir klar, dass Vlad seine aktualisieren musste, um eine Nachschlagetabelle zu haben, und sich ihr anzuschließen, um die ideale Ausgabe zu erhalten. Wenn Sie also nicht unangemessen sind, können Sie bitte Ihre aktualisieren, um dieselbe Suche einzuschließen? Dann würden alle 3 Antworten eine äquivalente Ausgabe ergeben (ideal zum Vergleichen aller 3), und ich würde Ihre akzeptieren. Ist das in Ordnung?
Solomon Rutzky
1
@srutzky Ich habe die Antwort aktualisiert. War vor einer Weile so hoffe ich habe es richtig gemacht und was suchst du? Sie können natürlich eine Ebenenspalte hinzufügen @Elemets, um für jede Ebene einen anderen Satz von Namen zur Auswahl zu erhalten.
Mikael Eriksson
1
@srutzky keine sorgen. Ich bin froh, dass die Antwort für Sie hilfreich war.
Mikael Eriksson
6

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:

ID int NOT NULL
ParentID int NULL
Lvl int NOT NULL

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).

CREATE TABLE [dbo].[Numbers](
    [Number] [int] NOT NULL,
CONSTRAINT [PK_Numbers] PRIMARY KEY CLUSTERED 
(
    [Number] ASC
));

INSERT INTO Numbers(Number)
SELECT TOP(1000)
    ROW_NUMBER() OVER(ORDER BY S.object_id)  AS Number
FROM
    sys.all_objects AS S
ORDER BY Number;

Parameter für die Datengenerierung sollten in einer Tabelle gespeichert werden:

DECLARE @Intervals TABLE (Lvl int, LvlMin int, LvlMax int);
INSERT INTO @Intervals (Lvl, LvlMin, LvlMax) VALUES
(1, 5, 20),
(2, 1, 10),
(3, 1, 5);

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, IDsum zu gewährleisten, dass diese Zeilen nicht wiederholt werden. Darum geht es POWER(100, CTE.Lvl)hier. Infolgedessen gibt es große Lücken IDs. Diese Zahl könnte eine sein MAX(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

CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5

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 gespeichert ChildRowCount.

Rekursiver Teil wird verwendet CROSS APPLY, um eine bestimmte Anzahl von untergeordneten Zeilen pro übergeordneter Zeile zu generieren. Ich musste WHERE Numbers.Number <= CTE.ChildRowCountstattdessen verwenden TOP(CTE.ChildRowCount), weil TOPes 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

WITH
CTE
AS
(
    SELECT 
        TOP(CAST(
            (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) * 
            (
                1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = 1)
                  - (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 1)
            )
            + (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 1)
            AS int))
        Numbers.Number AS ID
        ,NULL AS ParentID
        ,1 AS Lvl
        ,CAST(
            (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) * 
            (
                1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = 2)
                  - (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 2)
            )
            + (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 2)
            AS int) AS ChildRowCount
    FROM Numbers
    ORDER BY Numbers.Number

    UNION ALL

    SELECT
        CA.Number + CTE.ID * POWER(100, CTE.Lvl) AS ID
        ,CTE.ID AS ParentID
        ,CTE.Lvl + 1 AS Lvl
        ,CA.ChildRowCount
    FROM
        CTE
        CROSS APPLY
        (
            SELECT
                Numbers.Number
                ,CAST(
                    (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) * 
                    (
                    1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
                      - (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
                    )
                    + (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
                    AS int) AS ChildRowCount
            FROM Numbers
            WHERE Numbers.Number <= CTE.ChildRowCount
        ) AS CA
    WHERE
        CTE.ChildRowCount IS NOT NULL
)
SELECT *
FROM CTE
ORDER BY Lvl, ParentID, ID;

Ergebnis (mit etwas Glück kann es bis zu 20 + 20 * 10 + 200 * 5 = 1220 Zeilen geben)

+---------+----------+-----+-------------------+
|   ID    | ParentID | Lvl | ChildRowCount     |
+---------+----------+-----+-------------------+
|       1 | NULL     |   1 | 3                 |
|       2 | NULL     |   1 | 1                 |
|       3 | NULL     |   1 | 6                 |
|       4 | NULL     |   1 | 5                 |
|       5 | NULL     |   1 | 3                 |
|       6 | NULL     |   1 | 7                 |
|       7 | NULL     |   1 | 1                 |
|       8 | NULL     |   1 | 6                 |
|     101 | 1        |   2 | 3                 |
|     102 | 1        |   2 | 5                 |
|     103 | 1        |   2 | 1                 |
|     201 | 2        |   2 | 5                 |
|     301 | 3        |   2 | 4                 |
|     302 | 3        |   2 | 5                 |
|     303 | 3        |   2 | 1                 |
|     304 | 3        |   2 | 2                 |
|     305 | 3        |   2 | 4                 |
|     306 | 3        |   2 | 3                 |
|     401 | 4        |   2 | 3                 |
|     402 | 4        |   2 | 1                 |
|     403 | 4        |   2 | 2                 |
|     404 | 4        |   2 | 2                 |
|     405 | 4        |   2 | 4                 |
|     501 | 5        |   2 | 1                 |
|     502 | 5        |   2 | 3                 |
|     503 | 5        |   2 | 5                 |
|     601 | 6        |   2 | 2                 |
|     602 | 6        |   2 | 5                 |
|     603 | 6        |   2 | 3                 |
|     604 | 6        |   2 | 3                 |
|     605 | 6        |   2 | 4                 |
|     606 | 6        |   2 | 5                 |
|     607 | 6        |   2 | 4                 |
|     701 | 7        |   2 | 2                 |
|     801 | 8        |   2 | 2                 |
|     802 | 8        |   2 | 3                 |
|     803 | 8        |   2 | 3                 |
|     804 | 8        |   2 | 3                 |
|     805 | 8        |   2 | 5                 |
|     806 | 8        |   2 | 2                 |
| 1010001 | 101      |   3 | NULL              |
| 1010002 | 101      |   3 | NULL              |
| 1010003 | 101      |   3 | NULL              |
| 1020001 | 102      |   3 | NULL              |
| 1020002 | 102      |   3 | NULL              |
| 1020003 | 102      |   3 | NULL              |
| 1020004 | 102      |   3 | NULL              |
| 1020005 | 102      |   3 | NULL              |
| 1030001 | 103      |   3 | NULL              |
| 2010001 | 201      |   3 | NULL              |
| 2010002 | 201      |   3 | NULL              |
| 2010003 | 201      |   3 | NULL              |
| 2010004 | 201      |   3 | NULL              |
| 2010005 | 201      |   3 | NULL              |
| 3010001 | 301      |   3 | NULL              |
| 3010002 | 301      |   3 | NULL              |
| 3010003 | 301      |   3 | NULL              |
| 3010004 | 301      |   3 | NULL              |
| 3020001 | 302      |   3 | NULL              |
| 3020002 | 302      |   3 | NULL              |
| 3020003 | 302      |   3 | NULL              |
| 3020004 | 302      |   3 | NULL              |
| 3020005 | 302      |   3 | NULL              |
| 3030001 | 303      |   3 | NULL              |
| 3040001 | 304      |   3 | NULL              |
| 3040002 | 304      |   3 | NULL              |
| 3050001 | 305      |   3 | NULL              |
| 3050002 | 305      |   3 | NULL              |
| 3050003 | 305      |   3 | NULL              |
| 3050004 | 305      |   3 | NULL              |
| 3060001 | 306      |   3 | NULL              |
| 3060002 | 306      |   3 | NULL              |
| 3060003 | 306      |   3 | NULL              |
| 4010001 | 401      |   3 | NULL              |
| 4010002 | 401      |   3 | NULL              |
| 4010003 | 401      |   3 | NULL              |
| 4020001 | 402      |   3 | NULL              |
| 4030001 | 403      |   3 | NULL              |
| 4030002 | 403      |   3 | NULL              |
| 4040001 | 404      |   3 | NULL              |
| 4040002 | 404      |   3 | NULL              |
| 4050001 | 405      |   3 | NULL              |
| 4050002 | 405      |   3 | NULL              |
| 4050003 | 405      |   3 | NULL              |
| 4050004 | 405      |   3 | NULL              |
| 5010001 | 501      |   3 | NULL              |
| 5020001 | 502      |   3 | NULL              |
| 5020002 | 502      |   3 | NULL              |
| 5020003 | 502      |   3 | NULL              |
| 5030001 | 503      |   3 | NULL              |
| 5030002 | 503      |   3 | NULL              |
| 5030003 | 503      |   3 | NULL              |
| 5030004 | 503      |   3 | NULL              |
| 5030005 | 503      |   3 | NULL              |
| 6010001 | 601      |   3 | NULL              |
| 6010002 | 601      |   3 | NULL              |
| 6020001 | 602      |   3 | NULL              |
| 6020002 | 602      |   3 | NULL              |
| 6020003 | 602      |   3 | NULL              |
| 6020004 | 602      |   3 | NULL              |
| 6020005 | 602      |   3 | NULL              |
| 6030001 | 603      |   3 | NULL              |
| 6030002 | 603      |   3 | NULL              |
| 6030003 | 603      |   3 | NULL              |
| 6040001 | 604      |   3 | NULL              |
| 6040002 | 604      |   3 | NULL              |
| 6040003 | 604      |   3 | NULL              |
| 6050001 | 605      |   3 | NULL              |
| 6050002 | 605      |   3 | NULL              |
| 6050003 | 605      |   3 | NULL              |
| 6050004 | 605      |   3 | NULL              |
| 6060001 | 606      |   3 | NULL              |
| 6060002 | 606      |   3 | NULL              |
| 6060003 | 606      |   3 | NULL              |
| 6060004 | 606      |   3 | NULL              |
| 6060005 | 606      |   3 | NULL              |
| 6070001 | 607      |   3 | NULL              |
| 6070002 | 607      |   3 | NULL              |
| 6070003 | 607      |   3 | NULL              |
| 6070004 | 607      |   3 | NULL              |
| 7010001 | 701      |   3 | NULL              |
| 7010002 | 701      |   3 | NULL              |
| 8010001 | 801      |   3 | NULL              |
| 8010002 | 801      |   3 | NULL              |
| 8020001 | 802      |   3 | NULL              |
| 8020002 | 802      |   3 | NULL              |
| 8020003 | 802      |   3 | NULL              |
| 8030001 | 803      |   3 | NULL              |
| 8030002 | 803      |   3 | NULL              |
| 8030003 | 803      |   3 | NULL              |
| 8040001 | 804      |   3 | NULL              |
| 8040002 | 804      |   3 | NULL              |
| 8040003 | 804      |   3 | NULL              |
| 8050001 | 805      |   3 | NULL              |
| 8050002 | 805      |   3 | NULL              |
| 8050003 | 805      |   3 | NULL              |
| 8050004 | 805      |   3 | NULL              |
| 8050005 | 805      |   3 | NULL              |
| 8060001 | 806      |   3 | NULL              |
| 8060002 | 806      |   3 | NULL              |
+---------+----------+-----+-------------------+

Generieren eines vollständigen Pfads anstelle einer verknüpften Hierarchie

Wenn wir nur an den vollständigen Pfadebenen interessiert sind N, können wir IDund ParentIDaus dem CTE weglassen . Wenn wir eine Liste möglicher Namen in der Ergänzungstabelle haben Names, ist es einfach, sie aus dieser Tabelle in CTE auszuwählen. Die NamesTabelle 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.

DECLARE @Names TABLE (Lvl int, Name nvarchar(4000), SeqNumber int);

-- First level: AAA, BBB, CCC, etc.
INSERT INTO @Names (Lvl, Name, SeqNumber)
SELECT 1, REPLICATE(CHAR(Number+64), 3) AS Name, Number AS SeqNumber
FROM Numbers
WHERE Number <= 20;

-- Second level: 001, 002, 003, etc.
INSERT INTO @Names (Lvl, Name, SeqNumber)
SELECT 2, REPLACE(STR(Number, 3), ' ', '0') AS Name, Number AS SeqNumber
FROM Numbers
WHERE Number <= 10;

-- Third level: I, II, III, IV, V
INSERT INTO @Names (Lvl, Name, SeqNumber) VALUES
(3, 'I',   1),
(3, 'II',  2),
(3, 'III', 3),
(3, 'IV',  4),
(3, 'V',   5);

SQL Fiddle Hier ist die letzte Abfrage. Ich teilte das FullPathin FilePathund auf FileName.

WITH
CTE
AS
(
    SELECT 
        TOP(CAST(
            (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) * 
            (
                1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = 1)
                  - (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 1)
            )
            + (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 1)
            AS int))

        1 AS Lvl
        ,CAST(
            (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) * 
            (
                1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = 2)
                  - (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 2)
            )
            + (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 2)
            AS int) AS ChildRowCount
        ,N.Name AS FullPath
        ,N.Name AS [FilePath]
        ,CAST(N'' AS nvarchar(4000)) AS [FileName]
    FROM
        Numbers
        INNER JOIN @Names AS N ON 
            N.SeqNumber = Numbers.Number AND N.Lvl = 1
    ORDER BY Numbers.Number

    UNION ALL

    SELECT
        CTE.Lvl + 1 AS Lvl
        ,CA.ChildRowCount
        ,CTE.FullPath + '\' + CA.Name AS FullPath

        ,CASE WHEN CA.ChildRowCount IS NOT NULL 
            THEN CTE.FullPath + '\' + CA.Name
            ELSE CTE.FullPath END AS [FilePath]

        ,CASE WHEN CA.ChildRowCount IS NULL 
            THEN CA.Name
            ELSE N'' END AS [FileName]
    FROM
        CTE
        CROSS APPLY
        (
            SELECT
                Numbers.Number
                ,CAST(
                    (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) * 
                    (
                    1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
                      - (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
                    )
                    + (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
                    AS int) AS ChildRowCount
                ,N.Name
            FROM
                Numbers
                INNER JOIN @Names AS N ON 
                    N.SeqNumber = Numbers.Number AND N.Lvl = CTE.Lvl + 1
            WHERE Numbers.Number <= CTE.ChildRowCount
        ) AS CA
    WHERE
        CTE.ChildRowCount IS NOT NULL
)
SELECT
    CTE.FullPath
    ,CTE.[FilePath]
    ,CTE.[FileName]
FROM CTE
WHERE CTE.ChildRowCount IS NULL
ORDER BY FullPath;

Ergebnis

+-------------+----------+----------+
|  FullPath   | FilePath | FileName |
+-------------+----------+----------+
| AAA\001\I   | AAA\001  | I        |
| AAA\001\II  | AAA\001  | II       |
| AAA\002\I   | AAA\002  | I        |
| AAA\002\II  | AAA\002  | II       |
| AAA\002\III | AAA\002  | III      |
| AAA\002\IV  | AAA\002  | IV       |
| AAA\002\V   | AAA\002  | V        |
| AAA\003\I   | AAA\003  | I        |
| AAA\003\II  | AAA\003  | II       |
| AAA\003\III | AAA\003  | III      |
| AAA\004\I   | AAA\004  | I        |
| AAA\004\II  | AAA\004  | II       |
| AAA\004\III | AAA\004  | III      |
| AAA\004\IV  | AAA\004  | IV       |
| BBB\001\I   | BBB\001  | I        |
| BBB\001\II  | BBB\001  | II       |
| CCC\001\I   | CCC\001  | I        |
| CCC\001\II  | CCC\001  | II       |
| CCC\001\III | CCC\001  | III      |
| CCC\001\IV  | CCC\001  | IV       |
| CCC\001\V   | CCC\001  | V        |
| CCC\002\I   | CCC\002  | I        |
| CCC\003\I   | CCC\003  | I        |
| CCC\003\II  | CCC\003  | II       |
| CCC\004\I   | CCC\004  | I        |
| CCC\004\II  | CCC\004  | II       |
| CCC\005\I   | CCC\005  | I        |
| CCC\005\II  | CCC\005  | II       |
| CCC\005\III | CCC\005  | III      |
| CCC\006\I   | CCC\006  | I        |
| CCC\006\II  | CCC\006  | II       |
| CCC\006\III | CCC\006  | III      |
| CCC\006\IV  | CCC\006  | IV       |
| CCC\007\I   | CCC\007  | I        |
| CCC\007\II  | CCC\007  | II       |
| CCC\007\III | CCC\007  | III      |
| CCC\007\IV  | CCC\007  | IV       |
| CCC\008\I   | CCC\008  | I        |
| CCC\008\II  | CCC\008  | II       |
| CCC\008\III | CCC\008  | III      |
| CCC\009\I   | CCC\009  | I        |
| CCC\009\II  | CCC\009  | II       |
| CCC\009\III | CCC\009  | III      |
| CCC\009\IV  | CCC\009  | IV       |
| CCC\010\I   | CCC\010  | I        |
| CCC\010\II  | CCC\010  | II       |
| CCC\010\III | CCC\010  | III      |
| DDD\001\I   | DDD\001  | I        |
| DDD\001\II  | DDD\001  | II       |
| DDD\001\III | DDD\001  | III      |
| DDD\001\IV  | DDD\001  | IV       |
| DDD\002\I   | DDD\002  | I        |
| DDD\003\I   | DDD\003  | I        |
| DDD\003\II  | DDD\003  | II       |
| DDD\003\III | DDD\003  | III      |
| DDD\003\IV  | DDD\003  | IV       |
| DDD\004\I   | DDD\004  | I        |
| DDD\004\II  | DDD\004  | II       |
| DDD\004\III | DDD\004  | III      |
| DDD\005\I   | DDD\005  | I        |
| DDD\006\I   | DDD\006  | I        |
| DDD\006\II  | DDD\006  | II       |
| DDD\006\III | DDD\006  | III      |
| DDD\007\I   | DDD\007  | I        |
| DDD\007\II  | DDD\007  | II       |
| DDD\008\I   | DDD\008  | I        |
| DDD\008\II  | DDD\008  | II       |
| DDD\008\III | DDD\008  | III      |
| DDD\009\I   | DDD\009  | I        |
| DDD\009\II  | DDD\009  | II       |
| DDD\010\I   | DDD\010  | I        |
| DDD\010\II  | DDD\010  | II       |
| DDD\010\III | DDD\010  | III      |
| DDD\010\IV  | DDD\010  | IV       |
| DDD\010\V   | DDD\010  | V        |
| EEE\001\I   | EEE\001  | I        |
| EEE\001\II  | EEE\001  | II       |
| FFF\001\I   | FFF\001  | I        |
| FFF\002\I   | FFF\002  | I        |
| FFF\002\II  | FFF\002  | II       |
| FFF\003\I   | FFF\003  | I        |
| FFF\003\II  | FFF\003  | II       |
| FFF\003\III | FFF\003  | III      |
| FFF\003\IV  | FFF\003  | IV       |
| FFF\003\V   | FFF\003  | V        |
| FFF\004\I   | FFF\004  | I        |
| FFF\004\II  | FFF\004  | II       |
| FFF\004\III | FFF\004  | III      |
| FFF\004\IV  | FFF\004  | IV       |
| FFF\005\I   | FFF\005  | I        |
| FFF\006\I   | FFF\006  | I        |
| FFF\007\I   | FFF\007  | I        |
| FFF\007\II  | FFF\007  | II       |
| FFF\007\III | FFF\007  | III      |
| GGG\001\I   | GGG\001  | I        |
| GGG\001\II  | GGG\001  | II       |
| GGG\001\III | GGG\001  | III      |
| GGG\002\I   | GGG\002  | I        |
| GGG\003\I   | GGG\003  | I        |
| GGG\003\II  | GGG\003  | II       |
| GGG\003\III | GGG\003  | III      |
| GGG\004\I   | GGG\004  | I        |
| GGG\004\II  | GGG\004  | II       |
| HHH\001\I   | HHH\001  | I        |
| HHH\001\II  | HHH\001  | II       |
| HHH\001\III | HHH\001  | III      |
| HHH\002\I   | HHH\002  | I        |
| HHH\002\II  | HHH\002  | II       |
| HHH\002\III | HHH\002  | III      |
| HHH\002\IV  | HHH\002  | IV       |
| HHH\002\V   | HHH\002  | V        |
| HHH\003\I   | HHH\003  | I        |
| HHH\003\II  | HHH\003  | II       |
| HHH\003\III | HHH\003  | III      |
| HHH\003\IV  | HHH\003  | IV       |
| HHH\003\V   | HHH\003  | V        |
| HHH\004\I   | HHH\004  | I        |
| HHH\004\II  | HHH\004  | II       |
| HHH\004\III | HHH\004  | III      |
| HHH\004\IV  | HHH\004  | IV       |
| HHH\004\V   | HHH\004  | V        |
| HHH\005\I   | HHH\005  | I        |
| HHH\005\II  | HHH\005  | II       |
| HHH\005\III | HHH\005  | III      |
| HHH\005\IV  | HHH\005  | IV       |
| HHH\005\V   | HHH\005  | V        |
| HHH\006\I   | HHH\006  | I        |
| HHH\007\I   | HHH\007  | I        |
| HHH\007\II  | HHH\007  | II       |
| HHH\007\III | HHH\007  | III      |
| HHH\008\I   | HHH\008  | I        |
| HHH\008\II  | HHH\008  | II       |
| HHH\008\III | HHH\008  | III      |
| HHH\008\IV  | HHH\008  | IV       |
| HHH\008\V   | HHH\008  | V        |
+-------------+----------+----------+
Vladimir Baranov
quelle
Interessanter Ansatz :). Ich mag das. Können Sie der Vollständigkeit halber die Abfrage zum Auffüllen der Numbers-Tabelle (aus der SQL-Geige) hinzufügen oder diese einfach als Teil des CTE einschließen? Dann ist es einfacher für jemanden, einfach zu kopieren und einzufügen. Kann für diese Antwort die endgültige Ausgabe so ausgedrückt werden, dass jede Zeile für alle Level3-Werte ein vollständiger Pfad von Level1 bis Level3 ist? Ich denke, das würde INNER JOINim Finale nur 2 s dauern SELECT. 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.
Solomon Rutzky
Woher kommen diese Namen / Labels? Sollte ich eine 'Names'-Tabelle mit 20 Zeilen haben und einen Namen daraus auswählen? Somit würde auf jeder Ebene derselbe Satz von Namen erscheinen. Oder sollte jede Ebene einen eigenen Satz von Namen haben?
Vladimir Baranov
Ich denke, dass die Namen entweder aus einer Tabelle (temporär, real oder variabel) oder inline als Teil des CTE stammen können. Ich habe sie ursprünglich in den CTE eingefügt, sie dann jedoch in eine lokale temporäre Tabelle verschoben, damit der Hauptteil der Abfrage hier besser lesbar ist. Ich denke, mit der Struktur, die Sie haben, wäre es einfach genug, sich pro Ebene zu trennen. Aber wenn es nur ein Satz von 20 war, der auch ausreichen würde, würde es nur eine geringfügig geringere Variation in den Testdaten liefern. Die einzig wahre Voraussetzung ist, dass sich innerhalb eines Knotens kein Name wiederholt, da dies beim Versuch, die Verzeichnisse oder Dateien zu erstellen, zu Fehlern führen würde :).
Solomon Rutzky
1
@srutzky, ich habe eine zweite Variante hinzugefügt.
Vladimir Baranov
1
@srutzky, ich habe mich aufgeteilt FullPathin FilePathund FileName.
Vladimir Baranov
4

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 den CROSS APPLYs zum Laufen zu bringen (ich glaube, ich habe versucht, die Abfragen zu korrelieren, indem ich einen Wert aus dem übergeordneten Element als den nin dem verwendeten verwendete, TOP(n)aber dann war es nicht zufällig), entschied ich mich, einen Typ von "Zahlen" zu erstellen. Tabelle, die es einer INNER JOINoder WHEREBedingung ermöglicht, eine Menge von nElementen zu erzeugen, indem einfach eine Zahl zufällig ausgewählt und als angegeben wird WHERE 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 = 3erhalte 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 INSERTAnweisungen scrollen müssen, um zu den wenigen Zeilen der tatsächlichen Abfrage zu gelangen) habe ich sie in eine lokale temporäre Tabelle aufgeteilt.

IF (OBJECT_ID(N'tempdb..#Elements') IS NULL)
BEGIN
  PRINT 'Creating #Elements table...';
  CREATE TABLE #Elements (
     ElementLevel TINYINT NOT NULL,
     LevelName NVARCHAR(50) NOT NULL
                         );

  PRINT 'Populating #Elements table...';
  INSERT INTO #Elements (ElementLevel, LevelName)
    SELECT tmp.[Level], tmp.[Name]
    FROM (
                  SELECT 1,  N'Ella'
       UNION ALL  SELECT 2,  N'Itchy'
       UNION ALL  SELECT 2,  N'Scratchy'
       UNION ALL  SELECT 3,  N'Moe'
       UNION ALL  SELECT 3,  N'Larry'
       UNION ALL  SELECT 3,  N'Curly'
       UNION ALL  SELECT 4,  N'Ian'
       UNION ALL  SELECT 4,  N'Stephen'
       UNION ALL  SELECT 4,  N'Peter'
       UNION ALL  SELECT 4,  N'Bernard'
       UNION ALL  SELECT 5,  N'Michigan'
       UNION ALL  SELECT 5,  N'Erie'
       UNION ALL  SELECT 5,  N'Huron'
       UNION ALL  SELECT 5,  N'Ontario'
       UNION ALL  SELECT 5,  N'Superior'
       UNION ALL  SELECT 6,  N'White'
       UNION ALL  SELECT 6,  N'Orange'
       UNION ALL  SELECT 6,  N'Blonde'
       UNION ALL  SELECT 6,  N'Pink'
       UNION ALL  SELECT 6,  N'Blue'
       UNION ALL  SELECT 6,  N'Brown'
       UNION ALL  SELECT 7,  N'Asia'
       UNION ALL  SELECT 7,  N'Africa'
       UNION ALL  SELECT 7,  N'North America'
       UNION ALL  SELECT 7,  N'South America'
       UNION ALL  SELECT 7,  N'Antarctica'
       UNION ALL  SELECT 7,  N'Europe'
       UNION ALL  SELECT 7,  N'Australia'
       UNION ALL  SELECT 8,  N'AA'
       UNION ALL  SELECT 8,  N'BB'
       UNION ALL  SELECT 8,  N'CC'
       UNION ALL  SELECT 8,  N'DD'
       UNION ALL  SELECT 8,  N'EE'
       UNION ALL  SELECT 8,  N'FF'
       UNION ALL  SELECT 8,  N'GG'
       UNION ALL  SELECT 8,  N'HH'
       UNION ALL  SELECT 9,  N'I'
       UNION ALL  SELECT 9,  N'II'
       UNION ALL  SELECT 9,  N'III'
       UNION ALL  SELECT 9,  N'IV'
       UNION ALL  SELECT 9,  N'V'
       UNION ALL  SELECT 9,  N'VI'
       UNION ALL  SELECT 9,  N'VII'
       UNION ALL  SELECT 9,  N'VIII'
       UNION ALL  SELECT 9,  N'IX'
       UNION ALL  SELECT 10, N'Million'
       UNION ALL  SELECT 10, N'Billion'
       UNION ALL  SELECT 10, N'Trillion'
       UNION ALL  SELECT 10, N'Quadrillion'
       UNION ALL  SELECT 10, N'Quintillion'
       UNION ALL  SELECT 10, N'Sestillion'
       UNION ALL  SELECT 10, N'Sextillion'
       UNION ALL  SELECT 10, N'Octillion'
       UNION ALL  SELECT 10, N'Nonillion'
       UNION ALL  SELECT 10, N'Decillion'
     ) tmp([Level], [Name]);
END;

HAUPTABFRAGE

Für Level 1 habe ich nur [name]Werte herausgegriffen, sys.objectsda es dort immer viele Zeilen gibt. Wenn ich jedoch mehr Kontrolle über die Namen benötigte, konnte ich die #ElementsTabelle einfach erweitern , um zusätzliche Ebenen zu erhalten.

;WITH topdir(Level1, Randy) AS
(
    SELECT TOP ( (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 20) + 5 ) so.[name],
                ( (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 10) + 1 )
    FROM sys.objects so
    ORDER BY CRYPT_GEN_RANDOM(8) ASC
)
SELECT  td.Level1, tmp1.Level2, tmp2.Level3
FROM    topdir td
CROSS APPLY (SELECT help.LevelName, (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 5) + 1
            FROM #Elements help
            WHERE help.ElementLevel = td.Randy
            ) tmp1 (Level2, Bandy)
CROSS APPLY (SELECT help.LevelName
            FROM #Elements help
            WHERE help.ElementLevel = tmp1.Bandy
            ) tmp2 (Level3);

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.

DECLARE @Template NVARCHAR(4000);
SET @Template = N'<?xml version="1.0" encoding="ISO-8859-1"?>
<ns0:P4131 xmlns:ns0="http://switching/xi">
<R000000>
    <R00000010>R000000</R00000010>
    <R00000020>I</R00000020>
    <R00000030>{{Tag30}}</R00000030>
    <R00000040>{{Tag40}}</R00000040>
    <R00000050>{{Tag50}}</R00000050>
    <R00000060>2</R00000060>
</R000000>
</ns0:P4131>
';


;WITH topdir(Level1, Thing1) AS
(
    SELECT TOP ( (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 20) + 5 ) so.[name],
                ( (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 10) + 1 )
    FROM sys.objects so
    ORDER BY CRYPT_GEN_RANDOM(8) ASC
), main AS
(
   SELECT  td.Level1, tmp1.Level2, tmp2.Level3,
           td.Level1 + N'\' + tmp1.Level2 AS [FullPath],
           RIGHT('000' + CONVERT(VARCHAR(10),
                          (CONVERT(INT, CRYPT_GEN_RANDOM(2)) % 9999) + 1), 4) AS [R30],
           RIGHT('000' + CONVERT(VARCHAR(10),
                          (CONVERT(INT, CRYPT_GEN_RANDOM(2)) % 500) + 100), 4) AS [R50],
           ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS [RowNum]
   FROM    topdir td
   CROSS APPLY (SELECT help.LevelName, (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 5) + 1
                FROM #Elements help
                WHERE help.ElementLevel = td.Thing1
               ) tmp1 (Level2, Thing2)
   CROSS APPLY (SELECT help.LevelName
                FROM #Elements help
                WHERE help.ElementLevel = tmp1.Thing2
               ) tmp2 (Level3)
)
SELECT  mn.FullPath,
        mn.Level3 + N'.xml' AS [FileName],
        REPLACE(
            REPLACE(
                REPLACE(
                    @Template,
                    N'{{Tag30}}',
                    mn.R30),
                N'{{Tag40}}',
                mn.RowNum),
            N'{{Tag50}}',
            mn.R50) AS [Contents]
FROM    main mn;

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 SELECTdes CTE wie folgt geändert :

SELECT  SQL#.File_CreateDirectory(
            N'C:\Stuff\TestXmlFiles\' + mn.FullPath) AS [CreateTheDirectory],
        SQL#.File_WriteFile(
            N'C:\Stuff\TestXmlFiles\' + mn.FullPath + N'\' + mn.Level3 + N'.xml',
            REPLACE(
                REPLACE(
                    REPLACE(
                        @Template,
                        N'{{Tag30}}',
                        mn.R30),
                    N'{{Tag40}}',
                    mn.RowNum),
                N'{{Tag50}}',
                mn.R50), -- @FileData
            0, -- @AppendData
            '' -- @FileEncoding
                            ) AS [WriteTheFile]
FROM    main mn;
Solomon Rutzky
quelle