我有一个生产数据库,偶尔一个表中的冗余行需要“合并”。
我们假设此表中的两行除了ID之外都有相同的值。
Table "PrimaryStuff"
ID | SomeValue
1 | "I have value"
2 | "I have value"
3 | "I am different"
我们还假设存在许多相关表。由于重复项是在“PrimaryStuff”表中创建的,因此通常会在这些子表中创建行,这些行应该与PrimaryStuff表上的单个记录相关。 这些表的数量和名称不在我的控制之下,应该在运行时动态考虑。 IE:我不知道相关记录的名称甚至数量,因为其他人可能会编辑我不知道的数据库。
Table "ForeignStuff"
ID | PrimaryStuffId | LocalValue
1| 1| "I have the correct FK"
2| 1| "I have the correct FK"
3| 2| "I should get pointed to an FK of 1"
要解决PrimaryStuff的第1行和第2行的重复,我希望所有相关表将其FK更改为1,然后删除PrimaryStuff的第2行。这个应该是微不足道的,就好像PrimaryStuff的行一样1不存在,我可以将第2行的主键更新为1,更改将级联。我不能这样做,因为那将是PrimaryStuff唯一索引中的重复键。
随意提问,我会尽力清理任何令人困惑的事情。
答案 0 :(得分:2)
首先让我们获取需要更新的行列表(据我所知,您希望最低的ID替换所有更高的ID)
SELECT MIN(ID) OVER (PARTITION BY SomeValue ORDER BY SomeValue, ID ASC) AS FirstID,
ID,
SOMEVALUE
FROM PrimaryStuff
我们可以删除FirstID和ID匹配的那些,这些无关紧要
SELECT FirstID, ID FROM
(
SELECT MIN(ID) OVER (PARTITION BY SomeValue ORDER BY SomeValue, ID ASC) AS FirstID,
ID,
SOMEVALUE
FROM PrimaryStuff
) T
WHERE FirstID != ID
现在我们有一个更改列表。我们可以在更新语句中使用它,将它放在临时表(或下面的CTE)中:
WITH ChangeList AS
(
SELECT FirstID, ID FROM
(
SELECT MIN(ID) OVER (PARTITION BY SomeValue ORDER BY SomeValue, ID ASC) AS FirstID,
ID
FROM PrimaryStuff
) T
WHERE FirstID != ID
)
UPDATE ForeignStuff
SET PrimaryStuffId = ChangeList.FirstID
FROM ForeignStuff
JOIN ChangeList ON ForeignStuff.ID = ChangeList.ID
NB - 未经过测试的代码,可能有拼写错误。
答案 1 :(得分:1)
您是否可以更积极主动,并在SomeValue已存在时使用现有ID并对PrimaryStuff.SomeValue强制执行唯一约束,或者为什么不将SomeValue作为PrimaryStuff的主键。使用它作为PrimaryKey,那么如果SomeValue中不存在SomeValue,你只会向PrimaryStuff添加一条记录。
最后,最简单的说,如果SomeValue总是由其他人任意定义并且你接受他们给你的任何东西,为什么不完全放弃PrimaryStuff并让用户输入他们想要的任何东西到ForeignStuff?如果您需要SomeValue的唯一列表,请根据主表创建视图。如果需要加快查询速度,请在ForeignStuff.SomeValue字段中添加索引。
当有多个像ForeignStuff这样的表时,这是一个(未经测试的)视图:
-- dynamically generate a distinct list of values of interest
select SomeValue from ForeignStuffA
union select SomeValue from ForeignStuffB
union select SomeValue from ForeignStuffC
-- and so on, the union applies distinct