J'ai besoin de créer des données de test qui impliquent une hiérarchie. Je pourrais vous faciliter la tâche et faire quelques CROSS JOIN
s, mais cela me donnerait une structure complètement uniforme/sans aucune variation. Cela semble non seulement ennuyeux, mais le manque de variation dans les données de test masque parfois des problèmes qui seraient autrement trouvés. Donc, je veux générer une hiérarchie non uniforme qui suit ces règles:
WHILE
, la préférence est de trouver une approche basée sur un ensemble. De manière générale, la génération de données de test n'a pas les exigences d'efficacité que le code de production aurait, mais la recherche d'une approche basée sur un ensemble sera probablement plus éducative et aidera à l'avenir à trouver des approches basées sur un ensemble de problèmes. Les boucles WHILE
ne sont donc pas exclues, mais ne peuvent être utilisées que si aucune approche basée sur un ensemble n'est possible.[ CROSS | OUTER ] APPLY
, qui a été indiqué ci-dessus comme étant correct.Je ne sais pas si ces informations supplémentaires sont importantes, mais juste au cas où cela aiderait à avoir un certain contexte, les données de test se rapportent à ma réponse à cette question:
Importer des fichiers XML dans SQL Server 2012
Bien que cela ne soit pas pertinent à ce stade, l'objectif final de la génération de cette hiérarchie est de créer une structure de répertoires pour tester les méthodes récursives du système de fichiers. Les niveaux 1 et 2 seront des répertoires et le niveau 3 finira par être le nom du fichier. J'ai cherché autour (ici et via les Google) et je n'ai trouvé qu'une seule référence pour générer une hiérarchie aléatoire:
Linux: créer une hiérarchie aléatoire de répertoires/fichiers
Cette question (sur StackOverflow) est en fait assez proche en termes de résultat souhaité car cela cherche également à créer une structure de répertoires pour les tests. Mais cette question (et les réponses) se concentrent sur les scripts Linux/Unix Shell et pas tellement sur le monde basé sur les ensembles dans lequel nous vivons.
Maintenant, je sais comment générer des données aléatoires, et je le fais déjà pour créer le contenu des fichiers afin qu'ils puissent également afficher des variations. La partie délicate ici est que le nombre d'éléments dans chaque ensemble est aléatoire, pas un champ particulier. Et, le nombre d'éléments au sein de chaque nœud doit être aléatoire par rapport aux autres nœuds aux mêmes niveaux.
Exemple de hiérarchie
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
Exemple d'ensemble de résultats décrivant la hiérarchie ci-dessus
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
(Note d'O.P.: la solution préférée est le 4ème/dernier bloc de code)
XML me semble être le choix évident de la structure de données à utiliser ici.
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');
L'astuce pour que SQL Server utilise des valeurs différentes pour top()
pour chaque nœud est de corréler les sous-requêtes. N1.N > 0
Et N2.N > 0
.
Aplatir le XML:
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);
Et une version totalement vide de 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;
La corrélation N1.N > 0
Et N2.N > 0
Est toujours importante.
Une version utilisant une table avec 20 noms à utiliser au lieu de simples entiers.
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;
C'était intéressant.
Mon objectif était de générer un nombre donné de niveaux avec un nombre aléatoire de lignes enfants pour chaque niveau dans une structure hiérarchique correctement liée. Une fois que cette structure est prête, il est facile d'y ajouter des informations supplémentaires comme les noms de fichiers et de dossiers.
Donc, je voulais générer une table classique pour stocker un arbre:
ID int NOT NULL
ParentID int NULL
Lvl int NOT NULL
Puisque nous avons affaire à la récursivité, le CTE récursif semble un choix naturel.
J'aurai besoin d'un tableau des nombres . Les nombres dans le tableau doivent commencer à partir de 1. Il doit y avoir au moins 20 chiffres dans le tableau: 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;
Les paramètres de génération des données doivent être stockés dans une table:
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);
Notez que la requête est assez flexible et tous les paramètres sont séparés en un seul endroit. Vous pouvez ajouter plus de niveaux si nécessaire, ajoutez simplement une ligne supplémentaire de paramètres.
Pour rendre une telle génération dynamique possible, je devais me souvenir du nombre aléatoire de lignes pour le niveau suivant, j'ai donc une colonne supplémentaire ChildRowCount
.
La génération unique IDs
est également quelque peu délicate. J'ai codé en dur la limite de 100 lignes enfants pour 1 ligne parent pour garantir que IDs
ne se répète pas. C'est à cela que sert POWER(100, CTE.Lvl)
. Par conséquent, il existe de grandes lacunes dans IDs
. Ce nombre pourrait être une MAX(LvlMax)
, mais j'ai mis la constante 100 dans la requête pour plus de simplicité. Le nombre de niveaux n'est pas codé en dur, mais est déterminé par @Intervals
.
Cette formule
CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5
génère un nombre à virgule flottante aléatoire dans la plage [0..1)
, qui est ensuite mis à l'échelle à l'intervalle requis.
La logique de requête est simple. C'est récursif. La première étape génère un ensemble de lignes du premier niveau. Le nombre de lignes est déterminé par un nombre aléatoire dans TOP
. De plus, pour chaque ligne, un nombre aléatoire distinct de lignes enfants est stocké dans ChildRowCount
.
La partie récursive utilise CROSS APPLY
Pour générer un nombre donné de lignes enfants pour chaque ligne parent. J'ai dû utiliser WHERE Numbers.Number <= CTE.ChildRowCount
Au lieu de TOP(CTE.ChildRowCount)
, car TOP
n'est pas autorisé dans la partie récursive de CTE. Je ne connaissais pas cette limitation de SQL Server auparavant.
WHERE CTE.ChildRowCount IS NOT NULL
Arrête la récursivité.
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;
Résultat (il peut y avoir jusqu'à 20 + 20 * 10 + 200 * 5 = 1220 lignes si vous êtes chanceux)
+---------+----------+-----+-------------------+
| 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 |
+---------+----------+-----+-------------------+
Si nous ne sommes intéressés que par le chemin complet N
niveaux profonds, nous pouvons omettre ID
et ParentID
du CTE. Si nous avons une liste de noms possibles dans le tableau supplémentaire Names
, il est facile de les choisir dans ce tableau en CTE. La table Names
doit avoir suffisamment de lignes pour chaque niveau: 20 pour le niveau 1, 10 pour le niveau 2, 5 pour le niveau 3; 20 + 10 + 5 = 35 au total. Il n'est pas nécessaire d'avoir différents ensembles de lignes pour chaque niveau, mais il est facile de le configurer correctement, alors je l'ai fait.
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 Voici la requête finale. J'ai divisé le FullPath
en FilePath
et 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;
Résultat
+-------------+----------+----------+
| 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 |
+-------------+----------+----------+
Voici donc ce que j'ai trouvé. Dans le but de créer une structure de répertoires, je cherchais des "noms" utilisables pour les répertoires et les fichiers. Parce que je n'ai pas pu faire fonctionner la TOP(n)
dans le CROSS APPLY
(Je pense que j'ai tenté de corréler les requêtes en utilisant une valeur du parent comme n
dans le TOP(n)
mais ce n'était pas aléatoire), j'ai décidé de créer un type de table "nombres" qui permettrait à une condition INNER JOIN
ou WHERE
de produire un ensemble de n
éléments simplement en randomisant un nombre et en le spécifiant comme WHERE table.Level = random_number
. L'astuce est qu'il n'y a qu'une seule ligne pour le niveau 1, 2 lignes pour le niveau 2, 3 lignes pour le niveau 3, etc. Par conséquent, l'utilisation de WHERE LevelID = 3
Me donnera 3 lignes, et chaque ligne a une valeur que je peux utiliser comme nom de répertoire.
CONFIGURATION
Cette partie a été initialement spécifiée en ligne, dans le cadre du CTE. Mais pour des raisons de lisibilité (afin que vous n'ayez pas besoin de faire défiler de nombreuses instructions INSERT
pour accéder aux quelques lignes de la requête réelle), je l'ai éclaté dans une table temporaire locale.
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;
MAIN QUERY
Pour le niveau 1, je viens de saisir des valeurs de [name]
Dans sys.objects
Car il y a toujours beaucoup de lignes. Mais, si j'avais besoin de plus de contrôle sur les noms, je pouvais simplement développer la table #Elements
Pour contenir des niveaux supplémentaires.
;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);
REQUÊTE ADAPTÉE POUR PRODUIRE LE CHEMIN, LE NOM ET LE CONTENU DE CHAQUE FICHIER
Afin de générer les chemins complets pour les fichiers et le contenu des fichiers, j'ai fait le SELECT principal du CTE juste un autre CTE et ajouté un nouveau SELECT principal qui a donné les sorties appropriées qui ont simplement besoin d'entrer dans les fichiers.
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;
CRÉDIT SUPPLÉMENTAIRE
Bien que cela ne fasse pas partie des exigences énoncées dans la question, l'objectif (qui a été mentionné) était de créer des fichiers pour tester les fonctions récursives du système de fichiers avec. Alors, comment pouvons-nous prendre cet ensemble de résultats de noms de chemin, de noms de fichier et de contenu de fichier et faire quelque chose avec lui? Nous avons juste besoin de deux fonctions SQLCLR: une pour créer les dossiers et une pour créer les fichiers.
Afin de rendre ces données fonctionnelles, j'ai modifié le SELECT
principal du CTE montré directement ci-dessus comme suit:
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;