Muszę utworzyć dane testowe, które obejmują hierarchię. Mogę to ułatwić i zrobić kilkaCROSS JOIN
s, ale to dałoby mi strukturę, która jest całkowicie jednolita / bez żadnych zmian. To nie tylko wydaje się nudne, ale brak zmienności danych testowych czasami maskuje problemy, które w innym przypadku zostałyby odnalezione. Chcę więc wygenerować niejednorodną hierarchię, która będzie przestrzegać następujących zasad:
- 3 poziomy głębokości
- Poziom 1 to losowo 5–20 węzłów
- Poziom 2 to 1-10 węzłów losowo na każdy węzeł poziomu 1
- Poziom 3 to 1-5 węzłów losowo na każdy węzeł poziomu 2
- Wszystkie gałęzie będą miały 3 poziomy głębokości. Jednorodność głębokości jest w tym momencie w porządku.
- Nazwy węzłów podrzędnych mogą nakładać się na dowolnym poziomie (tzn. Nazwy węzłów podrzędnych nie muszą być unikalne we wszystkich węzłach na tym samym poziomie).
- Termin „losowy” zdefiniowano tutaj jako pseudolosowy, a nie wyjątkowo losowy. Należy o tym wspomnieć, ponieważ termin „losowy” jest często używany w znaczeniu „losowego uporządkowania danego zestawu, który nie wytwarza duplikatów”. Akceptuję to losowe = losowe i jeśli liczba dzieci na każdy węzeł poziomu 1 wynosi tylko 4, 7 i 8, nawet w 20 węzłach na poziomie 1, który ma potencjalny rozkład 1 - 10 dzieci na każdy z tych węzłów, to jest w porządku, ponieważ właśnie tym jest losowość.
- Chociaż można to zrobić dość łatwo za pomocą zagnieżdżonych
WHILE
pętli, preferowane jest znalezienie podejścia opartego na zestawie. Ogólnie rzecz biorąc, generowanie danych testowych nie ma wymagań dotyczących wydajności, jakie miałby kod produkcyjny, ale szukanie podejścia opartego na zestawie będzie prawdopodobnie bardziej edukacyjne i pomoże w przyszłości w znalezieniu opartych na zestawie podejść do problemów. WięcWHILE
pętle nie są wykluczone-out, ale może być stosowany tylko wtedy, gdy nie ma podejście oparte zestaw jest możliwe. - Oparte na zestawie = idealnie pojedyncze zapytanie, niezależnie od CTE, ZASTOSUJ itp. Tak więc używanie istniejącej lub wbudowanej tabeli liczb jest w porządku. Zastosowanie PODCZAS / KURSOR / podejście proceduralne nie będzie działać. Przypuszczam, że przenoszenie części danych do tabel tymczasowych lub zmiennych tabel jest w porządku, o ile wszystkie operacje są oparte na zestawie, bez pętli. Jednak powiedziawszy to, podejście oparte na jednym zapytaniu będzie prawdopodobnie preferowane w stosunku do wielu zapytań, chyba że można wykazać, że podejście oparte na wielu zapytaniach jest w rzeczywistości lepsze. Należy również pamiętać, że to, co stanowi „lepszy”, jest zazwyczaj subiektywne ;-). Należy również pamiętać, że użycie słowa „typowo” w poprzednim zdaniu jest również subiektywne.
- Każda wersja i edycja SQL Server (2005 i nowsze, jak sądzę) zrobi.
- Tylko czysty T-SQL: nic z tego głupiego SQLCLR !! Przynajmniej w zakresie generowania danych. Tworzenie katalogów i plików zostanie wykonane za pomocą SQLCLR. Ale tutaj koncentruję się na generowaniu wartości tego, co stworzyć.
- T-SQL Multi-statement TVF są uważane za proceduralne, a nie oparte na zestawie, mimo że na zewnątrz maskują podejście proceduralne w zestawie. Są chwile, kiedy jest to absolutnie właściwe. To nie jest jeden z tych czasów. Wzdłuż tych samych wierszy funkcje skalarne T-SQL również nie są dozwolone, nie tylko dlatego, że są również proceduralne, ale Optymalizator zapytań czasami buforuje swoją wartość i powtarza ją tak, że wynik nie jest zgodny z oczekiwaniami.
- Inline TVF T-SQL (aka iTVF) są w porządku, ponieważ są oparte na zestawach i faktycznie są takie same jak w użyciu
[ CROSS | OUTER ] APPLY
, co stwierdzono powyżej jako prawidłowe. - Powtarzające się wykonanie zapytania (zapytań) powinno dawać głównie inny wynik niż poprzednie uruchomienie.
- Wyjaśnienie Aktualizacja 1: Ostateczny zestaw wyników powinien być wyrażony jako posiadający jeden wiersz dla każdego odrębnego węzła poziomu 3, z pełną ścieżką rozpoczynającą się od poziomu 1. Oznacza to, że wartości Poziom 1 i Poziom 2 będą koniecznie powtarzać się w jednym lub większej liczbie wierszy, z wyjątkiem przypadków istnienia tylko jednego węzła Poziom 2 zawierającego tylko jeden węzeł Poziom 3.
- Wyjaśnienie Aktualizacja 2: Istnieje bardzo silna preferencja dla każdego węzła mającego nazwę lub etykietę, a nie tylko liczbę. Dzięki temu otrzymane dane testowe będą bardziej znaczące i realistyczne.
Nie jestem pewien, czy te dodatkowe informacje mają znaczenie, ale na wypadek, gdyby pomogło to w pewnym kontekście, dane testowe odnoszą się do mojej odpowiedzi na to pytanie:
Importuj pliki XML do SQL Server 2012
Chociaż nie jest to w tym momencie istotne, celem generowania tej hierarchii jest utworzenie struktury katalogów w celu przetestowania metod rekurencyjnych systemów plików. Poziomy 1 i 2 będą katalogami, a poziom 3 ostatecznie będzie nazwą pliku. Szukałem (zarówno tutaj, jak i przez Google) i znalazłem tylko jedno odniesienie do generowania losowej hierarchii:
Linux: utwórz losową hierarchię katalogów / plików
To pytanie (na StackOverflow) jest właściwie dość zbliżone pod względem pożądanego wyniku, ponieważ ma to również na celu stworzenie struktury katalogów do testowania. Ale to pytanie (i odpowiedzi) koncentruje się na skryptach powłoki Linux / Unix, a nie na świecie opartym na zestawie, w którym żyjemy.
Teraz wiem, jak generować losowe dane i już to robię, aby utworzyć zawartość plików, aby mogły one również wyświetlać odmiany. Problem polega na tym, że liczba elementów w każdym zestawie jest losowa, a nie konkretne pole. I liczba elementów w każdym węźle musi być losowa z innych węzłów na tych samych poziomach.
Przykładowa hierarchia
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
Przykładowy zestaw wyników opisujący powyższą hierarchię
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
źródło
TOP(n)
poprawnie działać w ciągu 2CROSS APPLY
sekund. Nie jestem pewien, co zrobiłem inaczej / niepoprawnie, odkąd pozbyłem się tego kodu, gdy dostałem coś innego do roboty. Wkrótce opublikuję tę aktualizację. I wyczyściłem większość moich komentarzy powyżej.n
elementów poprzez warunek WHERE, i 2) mamname
komponent, który jest bardziej kontrolowany niż losowe nazwy katalogów i / lub plików .@Elemets
aby uzyskać inny zestaw nazw dla każdego poziomu do wyboru.To było interesujące.
Moim celem było wygenerowanie określonej liczby poziomów z losową liczbą rzędów potomnych na każdy poziom w odpowiednio powiązanej strukturze hierarchicznej. Po przygotowaniu tej struktury łatwo jest dodać do niej dodatkowe informacje, takie jak nazwy plików i folderów.
Chciałem więc wygenerować klasyczny stół do przechowywania drzewa:
Ponieważ mamy do czynienia z rekurencją, rekurencyjna CTE wydaje się naturalnym wyborem.
Potrzebuję tabeli liczb . Liczby w tabeli powinien zacząć od 1. Nie powinno być co najmniej 20 numerów w tabeli:
MAX(LvlMax)
.Parametry do generowania danych powinny być przechowywane w tabeli:
Zauważ, że zapytanie jest dość elastyczne, a wszystkie parametry są rozdzielone w jednym miejscu. W razie potrzeby możesz dodać więcej poziomów, po prostu dodaj dodatkowy wiersz parametrów.
Aby możliwe było takie dynamiczne generowanie, musiałem pamiętać losową liczbę wierszy na następny poziom, więc mam dodatkową kolumnę
ChildRowCount
.Generowanie unikalnego
IDs
jest również nieco trudne. Zakodowałem na stałe limit 100 wierszy podrzędnych na 1 wiersz nadrzędny, aby zagwarantować, żeIDs
się nie powtórzą. O to właśniePOWER(100, CTE.Lvl)
chodzi. W rezultacie występują duże lukiIDs
. Ta liczba może być aMAX(LvlMax)
, ale dla uproszczenia wstawiłem stałą 100. Liczba poziomów nie jest zakodowana na stałe, ale zależy od@Intervals
.Ta formuła
generuje losową liczbę zmiennoprzecinkową w zakresie
[0..1)
, która jest następnie skalowana do wymaganego przedziału.Logika zapytań jest prosta. Jest rekurencyjny. Pierwszy krok generuje zestaw wierszy pierwszego poziomu. Liczba wierszy zależy od liczby losowej w
TOP
. Ponadto dla każdego wiersza jest przechowywana osobna losowa liczba wierszy podrzędnychChildRowCount
.Część rekurencyjna używa
CROSS APPLY
do generowania określonej liczby wierszy potomnych na każdy wiersz macierzysty. Musiałem użyćWHERE Numbers.Number <= CTE.ChildRowCount
zamiastTOP(CTE.ChildRowCount)
, ponieważTOP
nie jest dozwolone w rekurencyjnej części CTE. Nie wiedziałem wcześniej o tym ograniczeniu SQL Server.WHERE CTE.ChildRowCount IS NOT NULL
zatrzymuje rekurencję.SQL Fiddle
Wynik (możesz mieć do 20 + 20 * 10 + 200 * 5 = 1220 wierszy, jeśli masz szczęście)
Generowanie pełnej ścieżki zamiast połączonej hierarchii
Jeśli jesteśmy zainteresowani tylko
N
głębokimi poziomami pełnej ścieżki , możemy pominąćID
iParentID
z CTE. Jeśli mamy listę możliwych nazw w tabeli dodatkowejNames
, łatwo jest je wybrać z tej tabeli w CTE.Names
Tabela powinna mieć wystarczającą liczbę wierszy na każdym poziomie: 20 do poziomu 1, poziomu 2 10, 5 na poziomie 3; 20 + 10 + 5 = 35 ogółem. Nie trzeba mieć różnych zestawów wierszy dla każdego poziomu, ale łatwo go poprawnie skonfigurować, więc to zrobiłem.SQL Fiddle Oto ostatnie zapytanie. Podzieliłem
FullPath
naFilePath
iFileName
.Wynik
źródło
INNER JOIN
sekundy w finaleSELECT
. Wreszcie, czy nazwy / etykiety można przypisać do każdego węzła, aby nie były to tylko liczby? Zaktualizuję pytanie, aby wyjaśnić oba te punkty.FullPath
naFilePath
iFileName
.Oto co wymyśliłem. W celu stworzenia struktury katalogów szukałem użytecznych „nazw” dla katalogów i plików. Ponieważ nie udało mi się zdobyć
TOP(n)
poprawnego działaniaCROSS APPLY
(myślę, że próbowałem skorelować zapytania przy użyciu wartości nadrzędnej jakon
w,TOP(n)
ale nie było to przypadkowe), postanowiłem utworzyć rodzaj „liczb” stół, który pozwala osobieINNER JOIN
lubWHERE
warunku do produkcji zestawun
elementów po prostu wylosowaniu liczby i określając go jakoWHERE table.Level = random_number
. Sztuka polega na tym, że istnieje tylko 1 wiersz dla poziomu 1, 2 wiersze dla poziomu 2, 3 rzędy dla poziomu 3 i tak dalej. Dlatego za pomocąWHERE LevelID = 3
przyniesie mi 3 wiersze, a każdy wiersz ma wartość, której mogę użyć jako nazwy katalogu.USTAWIAĆ
Ta część została pierwotnie określona jako część CTE. Ale ze względu na czytelność (abyś nie musiał przewijać wielu
INSERT
instrukcji, aby przejść do kilku wierszy prawdziwego zapytania), podzieliłem go na lokalną tabelę tymczasową.GŁÓWNE ZAPYTANIE
Dla poziomu 1 właśnie złapałem
[name]
wartości,sys.objects
ponieważ zawsze jest tam dużo wierszy. Ale gdybym potrzebował większej kontroli nad nazwami, mógłbym po prostu rozszerzyć#Elements
tabelę o dodatkowe poziomy.ZAPYTANIE DOSTOSOWANE DO PRODUKCJI KAŻDEJ ŚCIEŻKI PLIKU, NAZWY I TREŚCI
Aby wygenerować pełne ścieżki do plików i zawartości pliku, dokonałem głównego WYBORU CTE tylko innym CTE i dodałem nowy główny WYBÓR, który dał odpowiednie dane wyjściowe, które po prostu muszą przejść do plików.
DODATKOWY KREDYT
Chociaż nie jest to część wymagań określonych w pytaniu, celem (o którym wspomniano) było utworzenie plików do testowania funkcji rekurencyjnych systemów plików. Jak więc wziąć ten zestaw wyników nazw ścieżek, nazw plików i zawartości plików i zrobić coś z tym? Potrzebujemy tylko dwóch funkcji SQLCLR: jednej do tworzenia folderów i jednej do tworzenia plików.
Aby te dane były funkcjonalne, zmodyfikowałem główną
SELECT
CTE pokazaną bezpośrednio powyżej w następujący sposób:źródło