J'ai besoin de créer des données de test qui impliquent une hiérarchie. Je pourrais le rendre facile 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 découverts. Donc, je veux générer une hiérarchie non uniforme qui suit ces règles:
- 3 niveaux de profondeur
- Le niveau 1 est aléatoire de 5 à 20 nœuds
- Le niveau 2 est de 1 à 10 nœuds, aléatoire pour chaque nœud du niveau 1
- Le niveau 3 est de 1 à 5 nœuds, aléatoire pour chaque nœud du niveau 2
- Toutes les branches auront 3 niveaux de profondeur. L'uniformité en profondeur est acceptable à ce stade.
- Il peut y avoir un chevauchement dans les noms des nœuds enfants à un niveau donné (c'est-à-dire que les noms des nœuds enfants n'ont pas besoin d'être uniques sur tous les nœuds du même niveau).
- Le terme "aléatoire" est défini ici comme étant pseudo-aléatoire, et non pas uniquement aléatoire. Cela doit être mentionné car le terme "aléatoire" est souvent utilisé pour signifier "un ordre aléatoire d'un ensemble donné qui ne produit pas de doublons". J'accepte que random = random et si le nombre d'enfants pour chaque nœud du niveau 1 n'est que de 4, 7 et 8, même sur 20 nœuds au niveau 1 qui a une propagation potentielle de 1 à 10 enfants pour chacun de ces nœuds, alors c'est très bien, parce que c'est ce qui est aléatoire.
- Même si cela peut être fait assez facilement avec des
WHILE
boucles imbriquées , 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. LesWHILE
boucles ne sont donc pas exclues, mais ne peuvent être utilisées que si aucune approche basée sur un ensemble n'est possible. - Basé sur un ensemble = idéalement une seule requête, indépendamment des CTE, APPLY, etc. L'utilisation d'une table de nombres existante ou en ligne est donc très bien. L'utilisation d'une approche WHILE / CURSOR / procédurale ne fonctionnera pas. Je suppose que le transfert de portions des données dans des tables temporaires ou des variables de table est correct, tant que les opérations sont toutes basées sur un ensemble, pas de boucles. Cependant, cela étant dit, une approche à requête unique sera probablement préférée à plusieurs requêtes, à moins qu'il ne puisse être démontré que l'approche à requêtes multiples est en fait meilleure. Veuillez également garder à l'esprit que ce qui constitue "mieux" est généralement subjectif ;-). Veuillez également garder à l'esprit que l'utilisation de "généralement" dans la phrase précédente est également subjective.
- N'importe quelle version et édition de SQL Server (2005 et plus récent, je suppose) fera l'affaire.
- Uniquement T-SQL pur: rien de tout ça idiot SQLCLR !! Au moins en termes de génération des données. La création des répertoires et des fichiers se fera à l'aide de SQLCLR. Mais ici, je me concentre uniquement sur la génération des valeurs de ce qu'il faut créer.
- Les TVF multi-instructions T-SQL sont considérées comme procédurales et non basées sur un ensemble, même si à l'extérieur elles masquent l'approche procédurale dans un ensemble. Il y a des moments où cela est tout à fait approprié. Ce n'est pas une de ces fois. Dans le même ordre d'idées, les fonctions scalaires T-SQL ne sont pas non plus autorisées, non seulement parce qu'elles sont également procédurales, mais l'Optimiseur de requête met parfois en cache leur valeur et la répète de sorte que la sortie ne soit pas comme prévu.
- Les TVF T-SQL Inline (aka iTVF) sont okey-dokey car ils sont basés sur des ensembles, et sont en fait les mêmes que l'utilisation
[ CROSS | OUTER ] APPLY
, ce qui a été indiqué ci-dessus comme étant ok. - Les exécutions répétées de la ou des requêtes devraient produire des résultats essentiellement différents de l'exécution précédente.
- Mise à jour de clarification 1: le jeu de résultats final doit être exprimé comme ayant une ligne pour chaque nœud distinct du niveau 3, avec le chemin complet commençant au niveau 1. Cela signifie que les valeurs Level1 et Level2 se répéteront nécessairement sur une ou plusieurs lignes, sauf dans le cas où il n'y a qu'un seul nœud Level2 contenant uniquement un seul nœud Level3.
- Mise à jour de clarification 2: Il y a une préférence très forte pour chaque nœud ayant un nom ou une étiquette, et pas seulement un numéro. Cela permettra aux données de test résultantes d'être plus significatives et réalistes.
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 du 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 shell Linux / Unix 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 à partir d'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
la source
TOP(n)
fonctionner correctement dans les 2CROSS APPLY
s. Je ne sais pas ce que j'ai fait différemment / incorrectement depuis que je me suis débarrassé de ce code une fois que j'ai fait fonctionner autre chose. Je posterai cela bientôt, maintenant que vous avez fourni cette mise à jour. Et j'ai nettoyé la plupart de mes commentaires ci-dessus.n
éléments via une condition WHERE, et 2) j'ai lename
composant qui est plus contrôlé que la randomisation des noms de répertoires et / ou de fichiers .@Elemets
pour obtenir un ensemble de noms différent pour chaque niveau à choisir.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:
Puisque nous avons affaire à la récursivité, le CTE récursif semble un choix naturel.
J'aurai besoin d'un tableau de chiffres . Les chiffres du tableau doivent commencer à partir de 1. Il devrait y avoir au moins 20 chiffres dans le tableau:
MAX(LvlMax)
.Les paramètres de génération des données doivent être stockés dans une table:
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, j'ai dû me souvenir du nombre aléatoire de lignes pour le niveau suivant, j'ai donc une colonne supplémentaire
ChildRowCount
.La génération d' 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 queIDs
cela ne se répète pas. C'est de cela qu'ilPOWER(100, CTE.Lvl)
s'agit. En conséquence, il existe de grandes lacunesIDs
. Ce nombre pourrait être unMAX(LvlMax)
, mais j'ai mis la constante 100 dans la requête de simplicité. Le nombre de niveaux n'est pas codé en dur, mais est déterminé par@Intervals
.Cette formule
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é dansChildRowCount
.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 à laWHERE Numbers.Number <= CTE.ChildRowCount
place deTOP(CTE.ChildRowCount)
, carTOP
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é.SQL Fiddle
Résultat (il peut y avoir jusqu'à 20 + 20 * 10 + 200 * 5 = 1220 lignes si vous êtes chanceux)
Génération d'un chemin complet au lieu d'une hiérarchie liée
Si nous ne sommes intéressés que par les
N
niveaux de chemin complets , nous pouvons omettreID
etParentID
quitter le CTE. Si nous avons une liste de noms possibles dans le tableau supplémentaireNames
, il est facile de les choisir dans ce tableau en CTE. LeNames
tableau 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.SQL Fiddle Voici la requête finale. Je partage l'
FullPath
enFilePath
etFileName
.Résultat
la source
INNER JOIN
s en finaleSELECT
. Enfin, des noms / étiquettes peuvent-ils être attribués à chaque nœud afin qu'ils ne soient pas uniquement des numéros? Je mettrai à jour la question pour clarifier ces deux points.FullPath
enFilePath
etFileName
.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 fichiers. Parce que je n'ai pas pu obtenir le
TOP(n)
travail dans leCROSS APPLY
s (je pense que j'ai essayé de corréler les requêtes en utilisant une valeur du parent commen
dans leTOP(n)
mais alors ce n'était pas aléatoire), j'ai décidé de créer un type de "nombres" table qui permettrait à une conditionINNER JOIN
orWHERE
de produire un ensemble d'n
éléments simplement en randomisant un nombre et en le spécifiant commeWHERE 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'utilisationWHERE LevelID = 3
me donnera 3 lignes, et chaque ligne a une valeur que je peux utiliser comme nom de répertoire.INSTALLER
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
INSERT
instructions pour accéder aux quelques lignes de la vraie requête), je l'ai éclaté dans une table temporaire locale.Requête principale
Pour le niveau 1, je viens de saisir des
[name]
valeurssys.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 étendre le#Elements
tableau pour contenir des niveaux supplémentaires.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.
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 fichiers et de contenu de fichiers et faire quelque chose avec? 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 principal
SELECT
du CTE montré directement ci-dessus comme suit:la source