Eu tenho uma tabela que pode ser criada e preenchida com o seguinte 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 as linhas que possuem uma distância de colaboração finita com base em RecordKey
outra linha, gostaria de atribuir um valor exclusivo - não me importo como ou que tipo de dados é o valor exclusivo.
Um conjunto de resultados correto que atenda ao que estou solicitando pode ser gerado com a seguinte 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 ajudar melhor o que estou perguntando, explicarei por que os GroupKey
1-3 são iguais SupergroupKey
:
GroupKey
1 contém oRecordKey
Euler que por sua vez está contido emGroupKey
2; portanto,GroupKey
s 1 e 2 devem ter o mesmoSupergroupKey
.- Como Gauss está contido em
GroupKey
s 2 e 3, eles também devem ter o mesmoSupergroupKey
. Isso leva a queGroupKey
s 1–3 tenha o mesmoSupergroupKey
. - Como
GroupKey
s 1–3 não compartilha nenhumRecordKey
s com osGroupKey
s restantes , eles são os únicos com oSupergroupKey
valor 1.
Devo acrescentar que a solução precisa ser genérica. A tabela acima e o conjunto de resultados foram apenas um exemplo.
Termo aditivo
Eu removi o requisito de a solução não ser iterativa. Embora eu prefira uma solução desse tipo, acredito que seja uma restrição irracional. Infelizmente, não consigo usar nenhuma solução baseada em CLR; mas se você quiser incluir essa solução, sinta-se à vontade. Eu provavelmente não aceitarei isso como resposta.
O número de linhas na minha tabela real é de até 5 milhões, mas há dias em que o número de linhas "apenas" será de cerca de dez mil. Em média, existem 8 RecordKey
s por GroupKey
e 4 GroupKey
s por RecordKey
. Imagino que uma solução tenha complexidade de tempo exponencial, mas, no entanto, estou interessado em uma solução.
fonte
Esse problema é sobre seguir os links entre os itens. Isso o coloca no domínio de gráficos e processamento de gráficos . Especificamente, todo o conjunto de dados forma um gráfico e estamos procurando componentes desse gráfico. Isso pode ser ilustrado por um gráfico dos dados de amostra da pergunta.
A pergunta diz que podemos seguir GroupKey ou RecordKey para encontrar outras linhas que compartilham esse valor. Assim, podemos tratar ambos como vértices em um gráfico. A pergunta continua para explicar como as teclas de grupo 1 a 3 têm a mesma tecla de supergrupo. Isso pode ser visto como o cluster à esquerda unido por linhas finas. A imagem também mostra os outros dois componentes (SupergroupKey) formados pelos dados originais.
O SQL Server possui alguma capacidade de processamento gráfico incorporada ao T-SQL. No momento, porém, é bastante escasso e não ajuda com esse problema. O SQL Server também pode chamar R e Python, e o rico e robusto conjunto de pacotes disponíveis para eles. Um deles é o gráfico . Foi escrito para "manipulação rápida de gráficos grandes, com milhões de vértices e arestas ( link )".
Usando R e igraph, fui capaz de processar um milhão de linhas em 2 minutos e 22 segundos no teste local 1 . É assim que ele se compara à melhor solução atual:
Ao processar 1 milhão de linhas, 1m40s foram usados para carregar e processar o gráfico e atualizar a tabela. 42s foram necessários para preencher uma tabela de resultados do SSMS com a saída.
A observação do Gerenciador de Tarefas enquanto as linhas de 1 milhão foram processadas sugere que são necessários cerca de 3 GB de memória de trabalho. Isso estava disponível neste sistema sem paginação.
Posso confirmar a avaliação de Ypercube da abordagem CTE recursiva. Com algumas centenas de chaves de registro, consumiu 100% da CPU e toda a RAM disponível. Eventualmente, o tempdb cresceu para mais de 80 GB e o SPID travou.
Eu usei a tabela de Paul com a coluna SupergroupKey, para que haja uma comparação justa entre as soluções.
Por alguma razão, R se opôs ao sotaque de Poincaré. Mudá-lo para um "e" simples permitiu a execução. Não investiguei, pois não é relevante para o problema em questão. Tenho certeza que há uma solução.
Aqui está o código
É isso que o código R faz
@input_data_1
é como o SQL Server transfere dados de uma tabela para o código R e os converte em um dataframe R chamado InputDataSet.library(igraph)
importa a biblioteca para o ambiente de execução R.df.g <- graph.data.frame(d = InputDataSet, directed = FALSE)
carregar os dados em um objeto igraph. Este é um gráfico não direcionado, pois podemos seguir os links do grupo para gravar ou gravar para o grupo. InputDataSet é o nome padrão do SQL Server para o conjunto de dados enviado para R.cpts <- components(df.g, mode = c("weak"))
processe o gráfico para encontrar subgráficos discretos (componentes) e outras medidas.OutputDataSet <- data.frame(cpts$membership)
O SQL Server espera um quadro de dados de volta de R. Seu nome padrão é OutputDataSet. Os componentes são armazenados em um vetor chamado "associação". Esta declaração converte o vetor em um quadro de dados.OutputDataSet$VertexName <- V(df.g)$name
V () é um vetor de vértices no gráfico - uma lista de Teclas de Grupo e Teclas de Registro. Isso os copia no quadro de dados de saída, criando uma nova coluna chamada VertexName. Essa é a chave usada para corresponder à tabela de origem para atualizar o SupergroupKey.Eu não sou um especialista em R. Provavelmente isso pode ser otimizado.
Dados de teste
Os dados do OP foram utilizados para validação. Para testes de escala, usei o seguinte script.
Acabei de perceber que entendi errado as proporções da definição do OP. Eu não acredito que isso afetará os horários. Registros e grupos são simétricos para esse processo. Para o algoritmo, todos são apenas nós em um gráfico.
No teste, os dados invariavelmente formaram um único componente. Eu acredito que isso se deve à distribuição uniforme dos dados. Se, em vez da proporção estática de 1: 8, codificada na rotina de geração, eu permitisse que a proporção variasse , provavelmente haveria outros componentes.
1 Especificação da máquina: Microsoft SQL Server 2017 (RTM-CU12), Developer Edition (64 bits), Windows 10 Home. 16GB de RAM, SSD, i7 hyperthreaded de 4 núcleos, 2,8 GHz nominal. Os testes foram os únicos itens em execução no momento, exceto a atividade normal do sistema (cerca de 4% da CPU).
fonte
Um método CTE recursivo - que provavelmente será terrivelmente ineficiente em grandes tabelas:
Testado em dbfiddle.uk
fonte