Вот еще один способ сделать это в пакете (без курсоров).@ KM выглядит так, как будто оно должно работать, но для меня это выглядит немного медленно / страшно с большим количеством операций блокировки и сканирования;если вы ограничиваете рабочий набор только новыми строками, то он должен быть довольно быстрым.
Вот сценарий установки для тестовых данных:
CREATE TABLE Colors
(
ColorID int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
ColorName varchar(50) NOT NULL
)
CREATE TABLE Markers
(
MarkerID int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
MarkerName varchar(50) NOT NULL,
ColorID int NOT NULL,
CONSTRAINT FK_Markers_Colors FOREIGN KEY (ColorID)
REFERENCES Colors (ColorID)
)
INSERT Colors (ColorName) VALUES ('Red')
INSERT Colors (ColorName) VALUES ('Green')
INSERT Colors (ColorName) VALUES ('Blue')
INSERT Markers (MarkerName, ColorID) VALUES ('Test1', 1)
INSERT Markers (MarkerName, ColorID) VALUES ('Test2', 1)
INSERT Markers (MarkerName, ColorID) VALUES ('Test3', 1)
INSERT Markers (MarkerName, ColorID) VALUES ('Test4', 2)
INSERT Markers (MarkerName, ColorID) VALUES ('Test5', 2)
INSERT Markers (MarkerName, ColorID) VALUES ('Test6', 3)
INSERT Markers (MarkerName, ColorID) VALUES ('Test7', 3)
Итак, у нас есть 1: Many имы хотим сделать это 1: 1.Чтобы сделать это, сначала поставьте в очередь список обновлений (мы индексируем это по некоторому другому множеству уникальных столбцов, чтобы ускорить объединение позже):
CREATE TABLE #NewColors
(
MarkerID int NOT NULL,
ColorName varchar(50) NOT NULL,
Seq int NOT NULL,
CONSTRAINT PK_#NewColors PRIMARY KEY (MarkerID)
)
CREATE INDEX IX_#NewColors
ON #NewColors (ColorName, Seq);
WITH Refs AS
(
SELECT
MarkerID,
ColorID,
ROW_NUMBER() OVER (PARTITION BY ColorID ORDER BY (SELECT 1)) AS Seq
FROM Markers
)
INSERT #NewColors (MarkerID, ColorName, Seq)
SELECT r.MarkerID, c.ColorName, r.Seq - 1
FROM Refs r
INNER JOIN Colors c
ON c.ColorID = r.ColorID
WHERE r.Seq > 1
Результат будет иметьодна строка для каждого маркера, который должен получить новый цвет.Затем вставьте новые цвета и получите полный вывод:
DECLARE @InsertedColors TABLE
(
ColorID int NOT NULL PRIMARY KEY,
ColorName varchar(50) NOT NULL
)
INSERT Colors (ColorName)
OUTPUT inserted.ColorID, inserted.ColorName
INTO @InsertedColors
SELECT ColorName
FROM #NewColors nc;
И, наконец, объедините его (вот где этот дополнительный индекс для временной таблицы пригодится):
WITH InsertedColorSeq AS
(
SELECT
ColorID, ColorName,
ROW_NUMBER() OVER (PARTITION BY ColorName ORDER BY ColorID) AS Seq
FROM @InsertedColors
),
Updates AS
(
SELECT nc.MarkerID, ic.ColorID AS NewColorID
FROM #NewColors nc
INNER JOIN InsertedColorSeq ic
ON ic.ColorName = nc.ColorName
AND ic.Seq = nc.Seq
)
MERGE Markers m
USING Updates u
ON m.MarkerID = u.MarkerID
WHEN MATCHED THEN
UPDATE SET m.ColorID = u.NewColorID;
DROP TABLE #NewColors
This должен быть очень эффективным, потому что он когда-либо запрашивает производственные таблицы только один раз.Все остальное будет работать с относительно небольшими данными в временных таблицах.
Проверьте результаты:
SELECT m.MarkerID, m.MarkerName, c.ColorID, c.ColorName
FROM Markers m
INNER JOIN Colors c
ON c.ColorID = m.ColorID
Вот наш вывод:
MarkerID MarkerName ColorID ColorName
1 Test1 1 Red
2 Test2 6 Red
3 Test3 7 Red
4 Test4 2 Green
5 Test5 5 Green
6 Test6 3 Blue
7 Test7 4 Blue
Это должно бытьчто хочешь, верно?Нет курсоров, нет серьезного безобразия.Если он отнимает слишком много памяти или места в базе данных tempdb, вы можете заменить переменную временная таблица / таблица индексированной физической промежуточной таблицей.Даже с несколькими миллионами строк это никоим образом не должно заполнить журнал транзакций и привести к сбою.