У меня есть таблица, которую можно создать и заполнить следующим кодом:
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');
Для всех строк с конечным расстоянием сотрудничества, основанным на RecordKey
другой строке, я хотел бы назначить уникальное значение - мне все равно, каким образом или каким типом данных является уникальное значение.
Правильный набор результатов, который соответствует тому, что я запрашиваю, может быть создан с помощью следующего запроса:
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;
Чтобы лучше понять то, что я прошу, я объясню, почему GroupKey
с 1 по 3 имеют то же самое SupergroupKey
:
GroupKey
1 содержитRecordKey
эйлер, который в свою очередь содержится вGroupKey
2; таким образом,GroupKey
s 1 и 2 должны иметь одинаковые значенияSupergroupKey
.- Поскольку гаусс содержится как в
GroupKey
s 2, так и в 3, они тоже должны быть одинаковымиSupergroupKey
. Это приводит к тому, чтоGroupKey
s 1–3 будут одинаковымиSupergroupKey
. - Поскольку
GroupKey
s 1–3 не делятRecordKey
s с остальнымиGroupKey
s, они единственные, которым присвоеноSupergroupKey
значение 1.
Я должен добавить, что решение должно быть общим. Приведенная выше таблица и набор результатов были просто примером.
добавление
Я убрал требование, чтобы решение было не итеративным. Хотя я бы предпочел такое решение, я считаю, что это неразумное ограничение. К сожалению, я не могу использовать какое-либо решение на основе CLR; но если вы хотите включить такое решение, не стесняйтесь. Я скорее всего не приму это как ответ.
Количество строк в моей реальной таблице достигает 5 миллионов, но бывают дни, когда число строк «всего» будет около десяти тысяч. В среднем 8 RecordKey
с GroupKey
и 4 GroupKey
с RecordKey
. Я полагаю, что решение будет иметь экспоненциальную временную сложность, но, тем не менее, я заинтересован в решении.
источник
Эта проблема касается следующих ссылок между элементами. Это помещает это в область графов и обработки графов. В частности, весь набор данных образует граф, и мы ищем компоненты этого графа. Это может быть проиллюстрировано графиком выборки данных из вопроса.
Вопрос говорит, что мы можем следовать GroupKey или RecordKey, чтобы найти другие строки, которые разделяют это значение. Таким образом, мы можем рассматривать обе вершины графа. Этот вопрос объясняет, почему у GroupKeys 1–3 одинаковый SupergroupKey. Это можно увидеть как кластер слева, соединенный тонкими линиями. На рисунке также показаны два других компонента (SupergroupKey), сформированные из исходных данных.
SQL Server имеет некоторые возможности обработки графиков, встроенные в T-SQL. В настоящее время это довольно скудно, но не помогает с этой проблемой. SQL Server также имеет возможность обращаться к R и Python, а также к богатому и надежному набору пакетов, доступных для них. Одним из таких является igraph . Он написан для «быстрой обработки больших графов с миллионами вершин и ребер ( ссылка )».
Используя R и igraph, я смог обработать миллион строк за 2 минуты 22 секунды в локальном тестировании 1 . Вот как он сравнивается с текущим лучшим решением:
При обработке 1М строк 1м40 использовались для загрузки и обработки графика, а также для обновления таблицы. 42 были обязаны заполнить таблицу результатов SSMS с выводом.
Наблюдение за диспетчером задач при обработке 1М строк позволяет предположить, что требовалось около 3 ГБ рабочей памяти. Это было доступно в этой системе без подкачки страниц.
Я могу подтвердить оценку Ypercube рекурсивного подхода CTE. С несколькими сотнями ключей записи он потребляет 100% процессорного времени и всей доступной оперативной памяти. В конце концов, tempdb выросла до 80 ГБ, и SPID потерпел крах.
Я использовал таблицу Пола со столбцом SupergroupKey, чтобы было справедливое сравнение между решениями.
По какой-то причине Р. возражал против акцента на Пуанкаре. Изменение его на обычное «е» позволило ему работать. Я не расследовал, так как это не относится к рассматриваемой проблеме. Я уверен, что есть решение.
Вот код
Это то, что делает код R
@input_data_1
это то, как SQL Server передает данные из таблицы в код R и переводит их в кадр данных R с именем InputDataSet.library(igraph)
импортирует библиотеку в среду выполнения R.df.g <- graph.data.frame(d = InputDataSet, directed = FALSE)
загрузить данные в объект igraph. Это неориентированный график, поскольку мы можем переходить по ссылкам от группы к записи или записи к группе. InputDataSet - это имя SQL Server по умолчанию для набора данных, отправляемого в R.cpts <- components(df.g, mode = c("weak"))
обработать график, чтобы найти дискретные подграфы (компоненты) и другие меры.OutputDataSet <- data.frame(cpts$membership)
SQL Server ожидает фрейм данных от R. Его имя по умолчанию - OutputDataSet. Компоненты хранятся в векторе, называемом «членство». Этот оператор переводит вектор во фрейм данных.OutputDataSet$VertexName <- V(df.g)$name
V () - это вектор вершин графа - список GroupKeys и RecordKeys. Это копирует их в выходной фрейм данных, создавая новый столбец с именем VertexName. Этот ключ используется для сопоставления с исходной таблицей для обновления SupergroupKey.Я не эксперт по R Скорее всего, это можно оптимизировать.
Тестовые данные
Данные ОП были использованы для проверки. Для масштабных тестов я использовал следующий скрипт.
Я только сейчас понял, что неправильно понял соотношения из определения ОП. Я не верю, что это повлияет на время. Записи и группы симметричны этому процессу. По алгоритму они все просто узлы на графике.
При тестировании данных неизменно формируется единый компонент. Я считаю, что это связано с равномерным распределением данных. Если бы вместо статического соотношения 1: 8, жестко запрограммированного в процедуру генерации, я бы позволил этому соотношению изменяться , скорее всего, были бы дополнительные компоненты.
1 Спецификация машины: Microsoft SQL Server 2017 (RTM-CU12), Developer Edition (64-разрядная версия), Windows 10 Home. 16 ГБ ОЗУ, твердотельный накопитель, 4-ядерный Hyper-Threading i7, номинал 2,8 ГГц. Тесты были единственными элементами, запущенными в то время, кроме нормальной системной активности (около 4% ЦП).
источник
Рекурсивный метод CTE - который, вероятно, будет ужасно неэффективным в больших таблицах:
Протестировано в dbfiddle.uk
источник