Tengo una tabla que se puede crear y completar con el siguiente código:
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');
Para todas las filas que tienen una distancia de colaboración finita basada en RecordKey
otra fila, me gustaría asignar un valor único; no me importa cómo o qué tipo de datos es el valor único.
Se puede generar un conjunto de resultados correcto que cumpla con lo que estoy pidiendo con la siguiente consulta:
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;
Para ayudar mejor a lo que pregunto, explicaré por qué GroupKey
s 1–3 tienen lo mismo SupergroupKey
:
GroupKey
1 contiene elRecordKey
Euler que a su vez está contenido enGroupKey
2; entoncesGroupKey
s 1 y 2 deben tener lo mismoSupergroupKey
.- Como Gauss está contenido tanto en
GroupKey
s 2 como en 3, ellos también deben tener lo mismoSupergroupKey
. Esto lleva aGroupKey
s 1–3 a tener lo mismoSupergroupKey
. - Dado que
GroupKey
s 1–3 no comparte ningunaRecordKey
s con lasGroupKey
s restantes , son las únicas que tienen asignado unSupergroupKey
valor de 1.
Debo agregar que la solución debe ser genérica. La tabla anterior y el conjunto de resultados fue simplemente un ejemplo.
Apéndice
Eliminé el requisito de que la solución no sea iterativa. Si bien preferiría tal solución, creo que es una restricción irrazonable. Desafortunadamente, no puedo usar ninguna solución basada en CLR; pero si desea incluir tal solución, no dude en hacerlo. Sin embargo, es probable que no lo acepte como respuesta.
El número de filas en mi tabla real es tan grande como 5 millones, pero hay días en que el número de filas "solo" será de alrededor de diez mil. En promedio hay 8 RecordKey
s por GroupKey
y 4 GroupKey
s por RecordKey
. Me imagino que una solución tendrá una complejidad de tiempo exponencial, pero de todos modos estoy interesado en una solución.
fuente
Este problema se trata de seguir enlaces entre elementos. Esto lo coloca en el ámbito de los gráficos y el procesamiento de gráficos. Específicamente, todo el conjunto de datos forma un gráfico y estamos buscando componentes de ese gráfico. Esto se puede ilustrar con una gráfica de los datos de muestra de la pregunta.
La pregunta dice que podemos seguir GroupKey o RecordKey para encontrar otras filas que compartan ese valor. Entonces podemos tratar a ambos como vértices en un gráfico. La pregunta continúa para explicar cómo GroupKeys 1–3 tienen la misma SupergroupKey. Esto se puede ver como el grupo a la izquierda unido por líneas finas. La imagen también muestra los otros dos componentes (SupergroupKey) formados por los datos originales.
SQL Server tiene alguna capacidad de procesamiento de gráficos integrada en T-SQL. Sin embargo, en este momento es bastante escaso y no ayuda con este problema. SQL Server también tiene la capacidad de llamar a R y Python, y al conjunto de paquetes rico y robusto disponible para ellos. Uno de ellos es igraph . Está escrito para "el manejo rápido de gráficos grandes, con millones de vértices y bordes ( enlace )".
Usando R e igraph pude procesar un millón de filas en 2 minutos y 22 segundos en la prueba local 1 . Así es como se compara con la mejor solución actual:
Al procesar filas de 1M, se usaron 1m40 para cargar y procesar el gráfico, y para actualizar la tabla. Se requirieron 42 para completar una tabla de resultados SSMS con la salida.
La observación del Administrador de tareas mientras se procesaban 1 millón de filas sugiere que se necesitaban unos 3 GB de memoria de trabajo. Esto estaba disponible en este sistema sin paginación.
Puedo confirmar la evaluación de Ypercube del enfoque recursivo de CTE. Con unos pocos cientos de claves de grabación consumió el 100% de la CPU y toda la RAM disponible. Finalmente, tempdb creció a más de 80 GB y el SPID se bloqueó.
Usé la tabla de Paul con la columna SupergroupKey para que haya una comparación justa entre las soluciones.
Por alguna razón, R se opuso al acento en Poincaré. Cambiarlo a una simple "e" le permitió ejecutarse. No investigué ya que no está relacionado con el problema en cuestión. Estoy seguro de que hay una solución.
Aqui esta el codigo
Esto es lo que hace el código R
@input_data_1
es cómo SQL Server transfiere datos de una tabla a código R y los traduce a un marco de datos R llamado InputDataSet.library(igraph)
importa la biblioteca al entorno de ejecución de R.df.g <- graph.data.frame(d = InputDataSet, directed = FALSE)
cargar los datos en un objeto igraph. Este es un gráfico no dirigido ya que podemos seguir enlaces de grupo a registro o grabar a grupo. InputDataSet es el nombre predeterminado de SQL Server para el conjunto de datos enviado a R.cpts <- components(df.g, mode = c("weak"))
procesar el gráfico para encontrar sub-gráficos discretos (componentes) y otras medidas.OutputDataSet <- data.frame(cpts$membership)
SQL Server espera un marco de datos de R. Su nombre predeterminado es OutputDataSet. Los componentes se almacenan en un vector llamado "membresía". Esta declaración traduce el vector a un marco de datos.OutputDataSet$VertexName <- V(df.g)$name
V () es un vector de vértices en el gráfico, una lista de GroupKeys y RecordKeys. Esto los copia en el marco de datos de salida, creando una nueva columna llamada VertexName. Esta es la clave utilizada para coincidir con la tabla de origen para actualizar SupergroupKey.No soy un experto en R. Probablemente esto podría optimizarse.
Datos de prueba
Los datos del OP se utilizaron para la validación. Para las pruebas de escala utilicé el siguiente script.
Me acabo de dar cuenta de que obtuve las proporciones al revés de la definición del OP. No creo que esto afecte los tiempos. Los registros y grupos son simétricos a este proceso. Para el algoritmo, todos son nodos en un gráfico.
Al probar, los datos invariablemente formaron un solo componente. Creo que esto se debe a la distribución uniforme de los datos. Si en lugar de la relación estática 1: 8 codificada en la rutina de generación, hubiera permitido que la relación variara , probablemente habría habido más componentes.
1 Especificaciones de la máquina: Microsoft SQL Server 2017 (RTM-CU12), Developer Edition (64 bits), Windows 10 Home. 16 GB de RAM, SSD, i7 de 4 núcleos hyperthreaded, 2.8GHz nominal. Las pruebas fueron los únicos elementos que se ejecutaron en ese momento, aparte de la actividad normal del sistema (aproximadamente 4% de CPU).
fuente
Un método CTE recursivo, que probablemente sea terriblemente ineficiente en tablas grandes:
Probado en dbfiddle.uk
fuente