Preciso criar alguns dados de teste que envolvam uma hierarquia. Eu poderia facilitar e executar alguns CROSS JOIN
s, mas isso me daria uma estrutura completamente uniforme / sem nenhuma variação. Isso não apenas parece chato, mas a falta de variação nos dados de teste às vezes oculta problemas que, de outra forma, seriam encontrados. Então, eu estou querendo gerar uma hierarquia não uniforme que siga estas regras:
- 3 níveis de profundidade
- O nível 1 é aleatoriamente de 5 a 20 nós
- O nível 2 é de 1 a 10 nós, aleatório por cada nó do nível 1
- O nível 3 é de 1 a 5 nós, aleatório por cada nó do nível 2
- Todos os ramos terão 3 níveis de profundidade. A uniformidade em profundidade está boa neste momento.
- Pode haver sobreposição nos nomes dos nós filhos em um determinado nível (ou seja, os nomes dos nós filhos não precisam ser exclusivos em todos os nós no mesmo nível).
- O termo "aleatório" é definido aqui como sendo pseudo-aleatório, não exclusivamente aleatório. Isso precisa ser mencionado, pois o termo "aleatório" costuma ser usado para significar "ordenação aleatória de um determinado conjunto que não produz duplicatas". Eu aceito que random = random e se o número de filhos por cada nó do nível 1 for de apenas 4, 7 e 8, mesmo em 20 nós no nível 1, com uma possível expansão de 1 a 10 filhos por cada um desses nós, então tudo bem, porque é isso que é aleatório.
- Mesmo que isso possa ser feito facilmente com
WHILE
loops aninhados , a preferência é encontrar uma abordagem baseada em conjunto. De um modo geral, a geração de dados de teste não possui os requisitos de eficiência que o código de produção teria, mas a busca por uma abordagem baseada em conjuntos provavelmente será mais educativa e ajudará no futuro a encontrar abordagens baseadas em conjuntos para problemas. Portanto, osWHILE
loops não são descartados, mas só podem ser usados se nenhuma abordagem baseada em conjuntos for possível. - Baseado em conjunto = idealmente uma única consulta, independentemente de CTEs, APPLYs, etc. Portanto, o uso de uma tabela de números existentes ou embutidos é bom. Usar uma abordagem WHILE / CURSOR / procedural não funcionará. Suponho que a preparação de partes dos dados em tabelas temporárias ou variáveis de tabela seja adequada, desde que todas as operações sejam baseadas em conjuntos, sem loops. No entanto, dito isso, uma abordagem de consulta única provavelmente será favorecida em relação a várias consultas, a menos que seja possível mostrar que a abordagem de consulta múltipla é realmente melhor. Lembre-se também de que o que constitui "melhor" é geralmente subjetivo ;-). Lembre-se também de que o uso de "normalmente" na frase anterior também é subjetivo.
- Qualquer versão e edição do SQL Server (2005 e mais recente, suponho) servirá.
- Apenas T-SQL puro: nada daquelas coisas bobas do SQLCLR !! Pelo menos em termos de geração de dados. A criação dos diretórios e arquivos será feita usando SQLCLR. Mas aqui estou focando apenas na geração dos valores do que criar.
- O TVF de múltiplas instruções T-SQL é considerado processual, não baseado em conjunto, embora, por fora, oculte a abordagem processual em um conjunto. Há momentos em que isso é absolutamente apropriado. Este não é um daqueles momentos. Na mesma linha, as funções escalares do T-SQL também não são permitidas, não apenas porque também são processuais, mas o Query Optimizer às vezes armazena em cache seu valor e o repete, de modo que a saída não seja a esperada.
- Os TVFs embutidos T-SQL (também conhecidos como iTVFs) são okey-dokey, pois são baseados em conjuntos e efetivamente o mesmo que o uso
[ CROSS | OUTER ] APPLY
, que foi declarado acima como ok. - Execuções repetidas da (s) consulta (s) devem produzir resultados principalmente diferentes da execução anterior.
- Atualização de esclarecimentos 1: O conjunto de resultados finais deve ser expresso como tendo uma linha para cada nó distinto do Nível3, tendo o caminho completo começando no Nível1. Isso significa que os valores de Nível1 e Nível2 se repetirão necessariamente em uma ou mais linhas, exceto nos casos em que haja apenas um nó de Nível2 contendo apenas um nó de Nível3.
- Atualização de esclarecimento 2: Há uma preferência muito forte para cada nó que tenha um nome ou rótulo, e não apenas um número. Isso permitirá que os dados de teste resultantes sejam mais significativos e realistas.
Não tenho certeza se essas informações adicionais são importantes, mas, caso ajude a ter algum contexto, os dados do teste estão relacionados à minha resposta a esta pergunta:
Importar arquivos XML para o SQL Server 2012
Embora não seja relevante neste momento, o objetivo final de gerar essa hierarquia é criar uma estrutura de diretórios para testar os métodos recursivos do sistema de arquivos. Os níveis 1 e 2 serão diretórios e o Nível 3 será o nome do arquivo. Eu pesquisei (aqui e no Google) e encontrei apenas uma referência para gerar uma hierarquia aleatória:
Linux: criar hierarquia aleatória de diretório / arquivo
Essa pergunta (no StackOverflow) é realmente bastante próxima em termos de resultado desejado, uma vez que também busca criar uma estrutura de diretórios para teste. Mas essa pergunta (e as respostas) estão focadas no script de shell do Linux / Unix e não tanto no mundo baseado em conjuntos em que vivemos.
Agora, eu sei como gerar dados aleatórios, e já estou fazendo isso para criar o conteúdo dos arquivos, para que eles também possam mostrar variações. A parte complicada aqui é que o número de elementos dentro de cada conjunto é aleatório, não um campo específico. E , o número de elementos em cada nó precisa ser aleatório em relação a outros nós nos mesmos níveis.
Hierarquia de exemplo
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
Exemplo de conjunto de resultados que descreve a hierarquia acima
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
fonte
TOP(n)
trabalho corretamente nosCROSS APPLY
2s. Não sei o que fiz de maneira diferente / incorreta desde que me livrei desse código depois que consegui outra coisa funcionando. Vou postar isso em breve, agora que você forneceu esta atualização. E eu limpei a maioria dos meus comentários acima.n
elementos através de uma condição WHERE e 2) possuo oname
componente que é mais controlado do que o diretório aleatório e / ou nomes de arquivos .@Elemets
para obter um conjunto diferente de nomes para cada nível à sua escolha.Isso foi interessante.
Meu objetivo era gerar um determinado número de níveis com um número aleatório de linhas filhas por cada nível em uma estrutura hierárquica adequadamente vinculada. Quando essa estrutura estiver pronta, é fácil adicionar informações extras, como nomes de arquivos e pastas.
Então, eu queria gerar uma tabela clássica para armazenar uma árvore:
Como estamos lidando com recursão, a CTE recursiva parece uma escolha natural.
Vou precisar de uma tabela de números . Os números na tabela deve começar a partir de 1. Deve haver, pelo menos, 20 números na tabela:
MAX(LvlMax)
.Os parâmetros para geração de dados devem ser armazenados em uma tabela:
Observe que a consulta é bastante flexível e todos os parâmetros são separados em um único local. Você pode adicionar mais níveis, se necessário, basta adicionar uma linha extra de parâmetros.
Para possibilitar essa geração dinâmica, tive que me lembrar do número aleatório de linhas para o próximo nível, então tenho uma coluna extra
ChildRowCount
.Gerar exclusivo
IDs
também é um pouco complicado. Codifiquei o limite de 100 linhas filho por 1 linha pai para garantir queIDs
isso não se repita. É disso quePOWER(100, CTE.Lvl)
se trata. Como resultado, existem grandes lacunasIDs
. Esse número pode ser umMAX(LvlMax)
, mas coloquei constante 100 na consulta por simplicidade. O número de níveis não é codificado, mas é determinado por@Intervals
.Esta fórmula
gera um número de ponto flutuante aleatório no intervalo
[0..1)
, que é escalado para o intervalo necessário.A lógica da consulta é simples. É recursivo. O primeiro passo gera um conjunto de linhas do primeiro nível. O número de linhas é determinado pelo número aleatório em
TOP
. Além disso, para cada linha, há um número aleatório separado de linhas filho armazenadasChildRowCount
.A parte recursiva é usada
CROSS APPLY
para gerar um determinado número de linhas filhas por cada linha pai. Eu tive que usar emWHERE Numbers.Number <= CTE.ChildRowCount
vez deTOP(CTE.ChildRowCount)
, porqueTOP
não é permitido na parte recursiva do CTE. Não sabia sobre essa limitação do SQL Server antes.WHERE CTE.ChildRowCount IS NOT NULL
interrompe a recursão.SQL Fiddle
Resultado (pode haver até 20 + 20 * 10 + 200 * 5 = 1220 linhas, se você tiver sorte)
Gerando caminho completo em vez de hierarquia vinculada
Se estivermos interessados apenas nos
N
níveis do caminho completo , podemos omitirID
eParentID
do CTE. Se tivermos uma lista de nomes possíveis na tabela suplementarNames
, é fácil selecioná-los nessa tabela no CTE. ANames
tabela deve ter linhas suficientes para cada nível: 20 para o nível 1, 10 para o nível 2, 5 para o nível 3; 20 + 10 + 5 = 35 no total. Não é necessário ter conjuntos diferentes de linhas para cada nível, mas é fácil configurá-lo corretamente, então eu o fiz.SQL Fiddle Aqui está a consulta final. Eu divido o
FullPath
emFilePath
eFileName
.Resultado
fonte
INNER JOIN
s na finalSELECT
. Por fim, nomes / etiquetas podem ser atribuídos a cada nó para que não sejam apenas números? Vou atualizar a pergunta para esclarecer esses dois pontos.FullPath
emFilePath
eFileName
.Então aqui está o que eu criei. Com o objetivo de criar uma estrutura de diretórios, eu estava procurando por "nomes" utilizáveis para os diretórios e arquivos. Como não consegui
TOP(n)
trabalhar noCROSS APPLY
s (acho que tentei correlacionar as consultas usando um valor do pai comon
noTOP(n)
mas não foi aleatório), decidi criar um tipo de "números" tabela que permitiria que umaINNER JOIN
ouWHERE
condição produzisse um conjunto den
elementos simplesmente randomizando um número e especificando-o comoWHERE table.Level = random_number
. O truque é que há apenas 1 linha para o Nível1, 2 linhas para o Nível2, 3 linhas para o Nível3 e assim por diante. Portanto, usarWHERE LevelID = 3
me dará três linhas e cada linha tem um valor que eu posso usar como um nome de diretório.CONFIGURAÇÃO
Esta parte foi originalmente especificada inline, como parte do CTE. Mas, para facilitar a leitura (para que você não precise rolar muitas
INSERT
instruções para chegar às poucas linhas da consulta real), eu a dividi em uma tabela temporária local.CONSULTA PRINCIPAL
Para o nível 1, retirei os
[name]
valores,sys.objects
pois sempre há muitas linhas lá. Mas, se eu precisasse ter mais controle sobre os nomes, poderia expandir a#Elements
tabela para conter níveis adicionais.CONSULTA ADAPTADA PARA PRODUZIR O CAMINHO, O NOME E O CONTEÚDO DE ARQUIVOS
Para gerar os caminhos completos para os arquivos e o conteúdo do arquivo, fiz o SELECT principal do CTE apenas mais um CTE e adicionei um novo SELECT principal que fornecia as saídas apropriadas que simplesmente precisam ser inseridas nos arquivos.
CRÉDITO EXTRA
Embora não faça parte dos requisitos estabelecidos na pergunta, o objetivo (mencionado) era criar arquivos para testar as funções recursivas do sistema de arquivos. Então, como pegamos esse conjunto de resultados de nomes de caminhos, nomes de arquivos e conteúdo de arquivos e fazemos algo com ele? Nós apenas precisamos de duas funções SQLCLR: uma para criar as pastas e outra para criar os arquivos.
Para tornar esses dados funcionais, modifiquei o principal
SELECT
do CTE mostrado diretamente acima, da seguinte maneira:fonte