Necesito crear algunos datos de prueba que involucren una jerarquía. Podría facilitarlo y hacer un par de CROSS JOIN
segundos, pero eso me daría una estructura que es completamente uniforme / sin ninguna variación. Eso no solo parece aburrido, sino que la falta de variación en los datos de la prueba a veces oculta problemas que de otro modo se encontrarían. Por lo tanto, quiero generar una jerarquía no uniforme que siga estas reglas:
- 3 niveles de profundidad
- El nivel 1 es aleatoriamente 5-20 nodos
- El nivel 2 es de 1 a 10 nodos, aleatorio por cada nodo del nivel 1
- El nivel 3 es de 1 a 5 nodos, aleatorio por cada nodo del nivel 2
- Todas las ramas serán de 3 niveles de profundidad. La uniformidad en profundidad está bien en este momento.
- Puede haber superposición en los nombres de los nodos secundarios en cualquier nivel dado (es decir, los nombres de los nodos secundarios no necesitan ser únicos en todos los nodos en el mismo nivel).
- El término "aleatorio" se define aquí como pseudoaleatorio, no únicamente aleatorio. Esto debe mencionarse ya que el término "aleatorio" se usa a menudo para significar "ordenamiento aleatorio de un conjunto dado que no produce duplicados". Acepto que random = random y si el número de niños por cada nodo del Nivel 1 es solo 4, 7 y 8, incluso en 20 nodos en el Nivel 1 que tiene una propagación potencial de 1 a 10 niños por cada uno de esos nodos, entonces eso está bien, porque eso es lo que es aleatorio.
- Aunque esto se puede hacer con bastante facilidad con
WHILE
bucles anidados , la preferencia es encontrar un enfoque basado en conjuntos. En términos generales, generar datos de prueba no tiene los requisitos de eficiencia que tendría el código de producción, pero buscar un enfoque basado en conjuntos probablemente será más educativo y ayudará en el futuro a encontrar enfoques basados en conjuntos para los problemas. Por lo tanto, losWHILE
bucles no se descartan, pero solo se pueden usar si no es posible un enfoque basado en conjuntos. - Basado en conjuntos = idealmente una única consulta, independientemente de CTE, APLICAR, etc. Por lo tanto, usar una tabla de números existente o en línea está bien. El uso de un enfoque WHILE / CURSOR / procedimiento no funcionará. Supongo que organizar partes de los datos en tablas temporales o variables de tabla está bien, siempre que las operaciones estén basadas en conjuntos, sin bucles. Sin embargo, dicho esto, un enfoque de consulta única probablemente se verá favorecido sobre múltiples consultas, a menos que se pueda demostrar que el enfoque de consulta múltiple es realmente mejor. Tenga en cuenta también que lo que constituye "mejor" suele ser subjetivo ;-). Tenga en cuenta también que el uso de "típicamente" en la oración anterior también es subjetivo.
- Cualquier versión y edición de SQL Server (2005 y posterior, supongo) funcionará.
- Solo T-SQL puro: ¡ninguna de esas tonterías de SQLCLR! Al menos en términos de generación de datos. La creación de los directorios y archivos se realizará utilizando SQLCLR. Pero aquí solo me estoy enfocando en generar los valores de qué crear.
- Los TVF de múltiples declaraciones T-SQL se consideran procesales, no basados en conjuntos, aunque en el exterior enmascaran el enfoque procesal en un conjunto. Hay momentos en que eso es absolutamente apropiado. Este no es uno de esos momentos. En esa misma línea, las funciones escalares de T-SQL tampoco están permitidas, no solo porque también son de procedimiento, sino que el Optimizador de consultas a veces almacena en caché su valor y lo repite de modo que el resultado no sea el esperado.
- Los TVF en línea de T-SQL (también conocidos como iTVF) son okey-dokey ya que están basados en conjuntos y son efectivamente lo mismo que usar
[ CROSS | OUTER ] APPLY
, lo que se indicó anteriormente como correcto. - Las ejecuciones repetidas de la (s) consulta (s) deberían producir resultados en su mayoría diferentes de la ejecución anterior.
- Actualización de aclaración 1: el conjunto de resultados finales debe expresarse como que tiene una fila para cada nodo distinto de Level3, con la ruta completa que comienza en Level1. Esto significa que los valores de Nivel1 y Nivel2 se repetirán necesariamente en una o más filas, excepto en los casos en que haya un solo nodo de Nivel2 que contenga solo un nodo de Nivel3.
- Actualización de aclaración 2: existe una preferencia muy fuerte para que cada nodo tenga un nombre o etiqueta, y no solo un número. Esto permitirá que los datos de prueba resultantes sean más significativos y realistas.
No estoy seguro de si esta información adicional es importante, pero en caso de que ayude a tener algún contexto, los datos de la prueba se relacionan con mi respuesta a esta pregunta:
Importar archivos XML a SQL Server 2012
Si bien no es relevante en este punto, el objetivo final de generar esta jerarquía es crear una estructura de directorio para probar los métodos recursivos del sistema de archivos. Los niveles 1 y 2 serán directorios, y el nivel 3 terminará siendo el nombre del archivo. He buscado (tanto aquí como a través de Google) y solo he encontrado una referencia para generar una jerarquía aleatoria:
Linux: crea una jerarquía aleatoria de directorios / archivos
Esa pregunta (en StackOverflow) en realidad es bastante cercana en términos del resultado deseado, ya que también busca crear una estructura de directorio para las pruebas. Pero esa pregunta (y las respuestas) se centran en las secuencias de comandos de shell de Linux / Unix y no tanto en el mundo basado en conjuntos en el que vivimos.
Ahora, sé cómo generar datos aleatorios, y ya lo estoy haciendo para crear el contenido de los archivos para que también puedan mostrar variaciones. La parte difícil aquí es que el número de elementos dentro de cada conjunto es aleatorio, no un campo en particular. Y , el número de elementos dentro de cada nodo debe ser aleatorio de otros nodos en esos mismos niveles.
Jerarquía de ejemplo
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
Conjunto de resultados de ejemplo que describe la jerarquía anterior
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
fuente
TOP(n)
que funcionara correctamente en los 2CROSS APPLY
s. No estoy seguro de lo que hice de manera diferente / incorrecta ya que me deshice de ese código una vez que algo más funcionó. Lo publicaré pronto, ahora que ha proporcionado esta actualización. Y he limpiado la mayoría de mis comentarios anteriores.n
elementos a través de una condición WHERE, y 2) tengo elname
componente que está más controlado que aleatorizar directorios y / o nombres de archivos .@Elemets
para obtener un conjunto diferente de nombres para cada nivel para elegir.Eso fue interesante.
Mi objetivo era generar un número dado de niveles con un número aleatorio de filas secundarias por cada nivel en una estructura jerárquica correctamente vinculada. Una vez que esta estructura está lista, es fácil agregarle información adicional, como nombres de archivos y carpetas.
Entonces, quería generar una tabla clásica para almacenar un árbol:
Como estamos lidiando con la recursividad, el CTE recursivo parece una opción natural.
Necesitaré una tabla de números . Los números en la tabla debe comenzar desde 1. Debe haber por lo menos 20 números en la tabla:
MAX(LvlMax)
.Los parámetros para la generación de datos deben almacenarse en una tabla:
Tenga en cuenta que la consulta es bastante flexible y todos los parámetros están separados en un solo lugar. Puede agregar más niveles si es necesario, solo agregue una fila adicional de parámetros.
Para realizar dicha generación dinámica posible que tuve que recordar el número al azar de filas para el siguiente nivel, así que tengo una columna adicional
ChildRowCount
.Generar únicos
IDs
también es algo complicado. Codifiqué el límite de 100 filas secundarias por 1 fila principal para garantizar queIDs
no se repita. De estoPOWER(100, CTE.Lvl)
se trata. Como resultado, hay grandes brechas enIDs
. Ese número podría ser unMAX(LvlMax)
, pero puse constante 100 en la consulta por simplicidad. El número de niveles no está codificado, pero está determinado por@Intervals
.Esta formula
genera un número aleatorio de coma flotante en el rango
[0..1)
, que luego se escala al intervalo requerido.La lógica de consulta es simple. Es recursivo. El primer paso genera un conjunto de filas del primer nivel. El número de filas está determinado por un número aleatorio en
TOP
. Además, para cada fila hay un número aleatorio separado de filas secundarias almacenadasChildRowCount
.La parte recursiva se utiliza
CROSS APPLY
para generar un número determinado de filas secundarias por cada fila principal. Tuve que usar enWHERE Numbers.Number <= CTE.ChildRowCount
lugar deTOP(CTE.ChildRowCount)
, porqueTOP
no está permitido en la parte recursiva de CTE. No sabía sobre esta limitación de SQL Server antes.WHERE CTE.ChildRowCount IS NOT NULL
detiene la recursividadViolín de SQL
Resultado (puede haber hasta 20 + 20 * 10 + 200 * 5 = 1220 filas si tiene suerte)
Generando ruta completa en lugar de jerarquía vinculada
Si estamos interesados solo en los
N
niveles de ruta completos profundos, podemos omitirID
yParentID
desde el CTE. Si tenemos una lista de posibles nombres en la tabla complementariaNames
, es fácil elegirlos de esta tabla en CTE. LaNames
tabla debe tener suficientes filas para cada nivel: 20 para el nivel 1, 10 para el nivel 2, 5 para el nivel 3; 20 + 10 + 5 = 35 en total. No es necesario tener diferentes conjuntos de filas para cada nivel, pero es fácil configurarlo correctamente, así que lo hice.SQL Fiddle Aquí está la consulta final. Lo dividí
FullPath
enFilePath
yFileName
.Resultado
fuente
INNER JOIN
s en la finalSELECT
. Finalmente, ¿se pueden asignar nombres / etiquetas a cada nodo para que no sean solo números? Actualizaré la pregunta para aclarar ambos puntos.FullPath
enFilePath
yFileName
.Así que aquí está lo que se me ocurrió. Con el objetivo de crear una estructura de directorios, estaba buscando "nombres" utilizables para los directorios y archivos. Debido a que no pude
TOP(n)
trabajar en elCROSS APPLY
s (creo que intenté correlacionar las consultas usando un valor del padre comon
en elTOP(n)
pero no fue al azar), decidí crear un tipo de "números" tabla que permitiría que una condiciónINNER JOIN
oWHERE
produzca un conjunto den
elementos simplemente aleatorizando un número y especificándolo comoWHERE table.Level = random_number
. El truco es que solo hay 1 fila para el Nivel 1, 2 filas para el Nivel 2, 3 filas para el Nivel 3, y así sucesivamente. Por lo tanto, usarWHERE LevelID = 3
me dará 3 filas, y cada fila tiene un valor que puedo usar como nombre de directorio.PREPARAR
Esta parte se especificó originalmente en línea, como parte del CTE. Pero en aras de la legibilidad (para que no necesite desplazarse a través de muchas
INSERT
declaraciones para llegar a las pocas líneas de la consulta real), lo dividí en una tabla temporal local.CONSULTA PRINCIPAL
Para el Nivel 1, simplemente tomé
[name]
valores,sys.objects
ya que siempre hay muchas filas allí. Pero, si necesitaba más control sobre los nombres, podría expandir la#Elements
tabla para contener niveles adicionales.CONSULTA ADAPTADA PARA PRODUCIR CADA RUTA, NOMBRE Y CONTENIDO DE ARCHIVO
Para generar las rutas completas para los archivos y el contenido del archivo, hice el SELECT principal del CTE simplemente otro CTE y agregué un nuevo SELECT principal que proporcionó las salidas adecuadas que simplemente necesitan ir a los archivos.
CRÉDITO ADICIONAL
Si bien no forma parte de los requisitos establecidos en la pregunta, el objetivo (que se mencionó) era crear archivos para probar las funciones recursivas del Sistema de archivos. Entonces, ¿cómo tomamos este conjunto de resultados de nombres de ruta, nombres de archivo y contenido de archivo y hacemos algo con él? Solo necesitamos dos funciones SQLCLR: una para crear las carpetas y otra para crear los archivos.
Para que estos datos funcionen, modifiqué el principal
SELECT
del CTE que se muestra directamente arriba de la siguiente manera:fuente