Mam tabelę, którą można utworzyć i wypełnić następującym kodem:
CREATE TABLE dbo.Example(GroupKey int NOT NULL, RecordKey varchar(12) NOT NULL);
ALTER TABLE dbo.Example
ADD CONSTRAINT iExample PRIMARY KEY CLUSTERED(GroupKey ASC, RecordKey ASC);
INSERT INTO dbo.Example(GroupKey, RecordKey)
VALUES (1, 'Archimedes'), (1, 'Newton'), (1, 'Euler'), (2, 'Euler'), (2, 'Gauss'),
(3, 'Gauss'), (3, 'Poincaré'), (4, 'Ramanujan'), (5, 'Neumann'),
(5, 'Grothendieck'), (6, 'Grothendieck'), (6, 'Tao');
Dla wszystkich wierszy, które mają skończoną odległość współpracy w oparciu RecordKey
o inny wiersz, chciałbym przypisać unikalną wartość - nie obchodzi mnie, w jaki sposób lub jaki typ danych jest unikalną wartością.
Prawidłowy zestaw wyników, który spełnia to, o co proszę, można wygenerować za pomocą następującego zapytania:
SELECT 1 AS SupergroupKey, GroupKey, RecordKey
FROM dbo.Example
WHERE GroupKey IN(1, 2, 3)
UNION ALL
SELECT 2 AS SupergroupKey, GroupKey, RecordKey
FROM dbo.Example
WHERE GroupKey = 4
UNION ALL
SELECT 3 AS SupergroupKey, GroupKey, RecordKey
FROM dbo.Example
WHERE GroupKey IN(5, 6)
ORDER BY SupergroupKey ASC, GroupKey ASC, RecordKey ASC;
Aby lepiej pomóc w pytaniu, wyjaśnię, dlaczego GroupKey
s 1–3 mają to samo SupergroupKey
:
GroupKey
1 zawieraRecordKey
Euler, który z kolei jest zawarty wGroupKey
2; zatemGroupKey
s 1 i 2 muszą mieć to samoSupergroupKey
.- Ponieważ Gauss jest zawarty zarówno w
GroupKey
s 2, jak i 3, one również muszą mieć to samoSupergroupKey
. Prowadzi to do tego, żeGroupKey
s 1–3 ma to samoSupergroupKey
. - Ponieważ
GroupKey
s 1–3 nie dzielą żadnychRecordKey
z pozostałymiGroupKey
, są one jedynymi, którym przypisanoSupergroupKey
wartość 1.
Powinienem dodać, że rozwiązanie musi być ogólne. Powyższa tabela i zestaw wyników były jedynie przykładem.
Uzupełnienie
Usunąłem wymóg, aby rozwiązanie nie było iteracyjne. Chociaż wolałbym takie rozwiązanie, uważam, że jest to nieuzasadnione ograniczenie. Niestety nie mogę użyć żadnego rozwiązania opartego na CLR; ale jeśli chcesz dołączyć takie rozwiązanie, nie krępuj się. Prawdopodobnie jednak nie zaakceptuję tego jako odpowiedzi.
Liczba wierszy w mojej prawdziwej tabeli wynosi aż 5 milionów, ale są dni, kiedy liczba wierszy będzie „tylko” około dziesięciu tysięcy. Średnio jest 8 RecordKey
s na GroupKey
i 4 GroupKey
s na RecordKey
. Wyobrażam sobie, że rozwiązanie będzie miało wykładniczą złożoność czasową, ale mimo to interesuje mnie rozwiązanie.
źródło
Ten problem dotyczy podążania za linkami między elementami. To umieszcza go w dziedzinie grafów i przetwarzania grafów. W szczególności cały zestaw danych tworzy wykres i szukamy składników tego wykresu. Można to zilustrować wykresem przykładowych danych z pytania.
Pytanie mówi, że możemy śledzić GroupKey lub RecordKey, aby znaleźć inne wiersze o tej samej wartości. Możemy więc traktować oba jako wierzchołki na wykresie. Pytanie wyjaśnia, w jaki sposób GroupKeys 1–3 mają ten sam SupergroupKey. Można to postrzegać jako skupisko po lewej połączone cienkimi liniami. Zdjęcie pokazuje również dwa inne komponenty (SupergroupKey) utworzone przez oryginalne dane.
SQL Server ma wbudowane możliwości przetwarzania grafów w T-SQL. W tej chwili jest to jednak dość skąpe i nie pomaga w rozwiązaniu tego problemu. SQL Server ma również możliwość wywoływania R i Pythona oraz bogatego i solidnego pakietu dostępnych dla nich pakietów. Jednym z nich jest Igraph . Jest napisany z myślą o „szybkiej obsłudze dużych wykresów z milionami wierzchołków i krawędzi ( link )”.
Używając R i igraph byłem w stanie przetworzyć milion wierszy w ciągu 2 minut 22 sekund w testach lokalnych 1 . Oto porównanie z obecnym najlepszym rozwiązaniem:
Podczas przetwarzania 1M wierszy zastosowano 1m40s do załadowania i przetworzenia wykresu oraz do aktualizacji tabeli. 42s były wymagane do zapełnienia tabeli wyników SSMS danymi wyjściowymi.
Obserwacja Menedżera zadań podczas przetwarzania 1M wierszy sugeruje, że wymagane było około 3 GB pamięci roboczej. To było dostępne w tym systemie bez stronicowania.
Mogę potwierdzić ocenę Ypercube dotyczącą rekurencyjnego podejścia CTE. Przy kilkuset kluczach zapisu zużywał 100% procesora i całą dostępną pamięć RAM. Ostatecznie tempdb wzrosło do ponad 80 GB i SPID się zawiesił.
Użyłem tabeli Paula z kolumną SupergroupKey, więc istnieje sprawiedliwe porównanie między rozwiązaniami.
Z jakiegoś powodu R sprzeciwił się akcentowi na Poincaré. Zmiana na zwykłe „e” pozwoliła na uruchomienie. Nie badałem, ponieważ nie ma on istotnego wpływu na omawiany problem. Jestem pewien, że istnieje rozwiązanie.
Oto kod
Tak właśnie działa kod R.
@input_data_1
jest to, w jaki sposób SQL Server przesyła dane z tabeli do kodu R i tłumaczy je na ramkę danych R o nazwie InputDataSet.library(igraph)
importuje bibliotekę do środowiska wykonawczego R.df.g <- graph.data.frame(d = InputDataSet, directed = FALSE)
załadować dane do obiektu igraph. To jest nieukierunkowany wykres, ponieważ możemy podążać za linkami z grupy do zapisu lub zapisu do grupy. InputDataSet to domyślna nazwa programu SQL Server dla zestawu danych wysłanego do R.cpts <- components(df.g, mode = c("weak"))
przetwarza wykres, aby znaleźć dyskretne podgrupy (komponenty) i inne miary.OutputDataSet <- data.frame(cpts$membership)
SQL Server oczekuje ramki danych z powrotem od R. Jego domyślna nazwa to OutputDataSet. Komponenty są przechowywane w wektorze zwanym „członkostwem”. Ta instrukcja przekształca wektor w ramkę danych.OutputDataSet$VertexName <- V(df.g)$name
V () to wektor wierzchołków na wykresie - lista kluczy GroupKeys i RecordKeys. Spowoduje to skopiowanie ich do ramki danych wyjściowych, tworząc nową kolumnę o nazwie VertexName. Jest to klucz używany do dopasowania do tabeli źródłowej do aktualizacji SupergroupKey.Nie jestem ekspertem od R. Prawdopodobnie można to zoptymalizować.
Dane testowe
Dane PO zostały wykorzystane do walidacji. Do testów skali użyłem następującego skryptu.
Właśnie zdałem sobie sprawę, że źle zrozumiałem współczynniki z definicji PO. Nie wierzę, że wpłynie to na czasy. Rekordy i grupy są symetryczne dla tego procesu. Według algorytmu wszystkie są tylko węzłami na wykresie.
Podczas testowania dane niezmiennie tworzyły pojedynczy komponent. Uważam, że jest to spowodowane jednolitym rozkładem danych. Gdyby zamiast statycznego stosunku 1: 8 zakodowanego na stałe w procedurze generowania pozwoliłbym na zmianę tego stosunku , bardziej prawdopodobne byłyby dalsze elementy.
1 Specyfikacja komputera: Microsoft SQL Server 2017 (RTM-CU12), Developer Edition (64-bit), Windows 10 Home. 16 GB pamięci RAM, dysk SSD, 4-rdzeniowy hyperthreaded i7, 2,8 GHz nominalnie. Testy były jedynymi działającymi w tym czasie elementami, innymi niż normalna aktywność systemu (około 4% procesora).
źródło
Rekurencyjna metoda CTE - która może być okropnie nieefektywna w dużych tabelach:
Testowany w dbfiddle.uk
źródło