J'ai une table qui peut être créée et remplie avec le code suivant:
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');
Pour toutes les lignes qui ont une distance de collaboration finie basée sur RecordKey
une autre ligne, je voudrais attribuer une valeur unique — peu m'importe le type ou le type de données de la valeur unique.
Un jeu de résultats correct qui répond à ce que je demande peut être généré avec la requête suivante:
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;
Pour mieux aider ce que je demande, je vais expliquer pourquoi les GroupKey
articles 1 à 3 ont la même chose SupergroupKey
:
GroupKey
1 contient l'RecordKey
Euler qui à son tour est contenu dansGroupKey
2; doncGroupKey
s 1 et 2 doivent avoir la même choseSupergroupKey
.- Parce que Gauss est contenu à la fois dans
GroupKey
s 2 et 3, ils doivent aussi avoir la même choseSupergroupKey
. Cela conduit à ce que lesGroupKey
articles 1 à 3 soient identiquesSupergroupKey
. - Puisque les
GroupKey
s 1 à 3 ne partagent aucunRecordKey
s avec lesGroupKey
s restants , ils sont les seuls à avoir uneSupergroupKey
valeur de 1.
Je dois ajouter que la solution doit être générique. Le tableau et l'ensemble de résultats ci-dessus n'étaient qu'un exemple.
Addenda
J'ai supprimé l'exigence que la solution soit non itérative. Bien que je préfère une telle solution, je pense que c'est une contrainte déraisonnable. Malheureusement, je ne peux utiliser aucune solution basée sur CLR; mais si vous souhaitez inclure une telle solution, n'hésitez pas à. Je ne l'accepterai probablement pas comme réponse cependant.
Le nombre de lignes de ma vraie table peut atteindre 5 millions, mais il y a des jours où le nombre de lignes ne sera "que" d'environ dix mille. En moyenne, il y a 8 RecordKey
s par GroupKey
et 4 GroupKey
s par RecordKey
. J'imagine qu'une solution aura une complexité temporelle exponentielle, mais je suis néanmoins intéressé par une solution.
la source
Ce problème concerne les liens entre les éléments. Cela le place dans le domaine des graphiques et du traitement des graphiques. Plus précisément, l'ensemble de données forme un graphique et nous recherchons des composants de ce graphique. Cela peut être illustré par un tracé des données de l'échantillon de la question.
La question dit que nous pouvons suivre GroupKey ou RecordKey pour trouver d'autres lignes qui partagent cette valeur. Nous pouvons donc traiter les deux comme des sommets dans un graphique. La question continue pour expliquer comment les GroupKeys 1 à 3 ont la même SupergroupKey. Cela peut être vu comme le cluster à gauche rejoint par des lignes fines. L'image montre également les deux autres composants (SupergroupKey) formés par les données d'origine.
SQL Server a une certaine capacité de traitement graphique intégrée à T-SQL. À l'heure actuelle, il est cependant assez maigre et n'aide pas avec ce problème. SQL Server a également la possibilité d'appeler R et Python, ainsi que la suite riche et robuste de packages à leur disposition. Un tel est l' igraph . Il est écrit pour "une gestion rapide des grands graphes, avec des millions de sommets et d'arêtes ( lien )".
En utilisant R et igraph, j'ai pu traiter un million de lignes en 2 minutes 22 secondes dans les tests locaux 1 . Voici comment il se compare à la meilleure solution actuelle:
Lors du traitement de 1M de lignes, 1m40 a été utilisé pour charger et traiter le graphique et mettre à jour le tableau. Il a fallu 42 secondes pour remplir une table de résultats SSMS avec la sortie.
L'observation du gestionnaire de tâches pendant le traitement de 1 million de lignes suggère qu'il fallait environ 3 Go de mémoire de travail. C'était disponible sur ce système sans pagination.
Je peux confirmer l'évaluation par Ypercube de l'approche CTE récursive. Avec quelques centaines de clés d'enregistrement, il a consommé 100% du CPU et toute la RAM disponible. Finalement, tempdb est passé à plus de 80 Go et le SPID s'est écrasé.
J'ai utilisé la table de Paul avec la colonne SupergroupKey afin qu'il y ait une comparaison équitable entre les solutions.
Pour une raison quelconque, R s'est opposé à l'accent sur Poincaré. Le changer en un simple "e" lui a permis de s'exécuter. Je n'ai pas enquêté car ce n'est pas lié au problème actuel. Je suis sûr qu'il y a une solution.
Voici le code
C'est ce que fait le code R
@input_data_1
est la façon dont SQL Server transfère les données d'une table au code R et les convertit en une trame de données R appelée InputDataSet.library(igraph)
importe la bibliothèque dans l'environnement d'exécution R.df.g <- graph.data.frame(d = InputDataSet, directed = FALSE)
charger les données dans un objet igraph. Il s'agit d'un graphique non orienté car nous pouvons suivre les liens d'un groupe à l'autre ou d'un enregistrement à un autre. InputDataSet est le nom par défaut de SQL Server pour l'ensemble de données envoyé à R.cpts <- components(df.g, mode = c("weak"))
traiter le graphique pour trouver des sous-graphiques discrets (composants) et d'autres mesures.OutputDataSet <- data.frame(cpts$membership)
SQL Server attend une trame de données à partir de R. Son nom par défaut est OutputDataSet. Les composants sont stockés dans un vecteur appelé "appartenance". Cette instruction traduit le vecteur en un bloc de données.OutputDataSet$VertexName <- V(df.g)$name
V () est un vecteur de sommets dans le graphique - une liste de GroupKeys et RecordKeys. Cela les copie dans le bloc de données de sortie, créant une nouvelle colonne appelée VertexName. Il s'agit de la clé utilisée pour correspondre à la table source pour la mise à jour de SupergroupKey.Je ne suis pas un expert en R. Cela pourrait probablement être optimisé.
Données de test
Les données du PO ont été utilisées pour la validation. Pour les tests d'échelle, j'ai utilisé le script suivant.
Je viens juste de réaliser que j'ai obtenu les rapports dans le mauvais sens à partir de la définition de l'OP. Je ne pense pas que cela affectera les horaires. Les enregistrements et les groupes sont symétriques à ce processus. Pour l'algorithme, ce ne sont que des nœuds dans un graphique.
Lors des tests, les données formaient invariablement un seul composant. Je pense que cela est dû à la distribution uniforme des données. Si, au lieu du rapport statique 1: 8 codé en dur dans la routine de génération, j'avais permis au rapport de varier, il y aurait probablement eu d'autres composants.
1 Spécifications de la machine: Microsoft SQL Server 2017 (RTM-CU12), Developer Edition (64 bits), Windows 10 Home. 16 Go de RAM, SSD, i7 hyperthreadé à 4 cœurs, 2,8 GHz nominal. Les tests étaient les seuls éléments exécutés à l'époque, à l'exception de l'activité normale du système (environ 4% du processeur).
la source
Une méthode CTE récursive - qui risque d'être terriblement inefficace dans les grands tableaux:
Testé dans dbfiddle.uk
la source