这是批量执行此操作的另一种方法(无游标)。@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
这应该非常有效,因为它只需要查询一次生产表。其他一切都将在临时表中相对较小的数据上运行。
测试结果:
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 空间,那么您可以用索引的物理临时表替换临时表/表变量。即使有几百万行,也不可能填满事务日志并崩溃。