這裏的另一種方式在批處理(無光標)來做到這一點。 @ 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:很多,我們希望使之成爲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空間,那麼可以用索引的物理臨時表替換臨時表/表變量。即使有數百萬行,這也不應該填滿事務日誌和崩潰。
只是爲了解:你想改變多對一的關係(許多A到B)成一對一的關係(一個A到一個B)而不填滿你的日誌? – NotMe 2010-03-05 16:13:10
我們在這裏討論的有多大?我真的懷疑問題在於你正在使用一個遊標來完成一些可能通過一些'INSERT'和'UPDATE'語句完成的事情。 – Aaronaught 2010-03-05 16:13:56
@Chris:是的,你是對的,這就是我們想要做的;) – 2010-03-05 16:14:37