T-SQL“合并”两行,或“重新生成”所有FK关系

时间:2013-09-17 15:22:20

标签: sql sql-server tsql

我有一个生产数据库,偶尔一个表中的冗余行需要“合并”。

我们假设此表中的两行除了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唯一索引中的重复键。

随意提问,我会尽力清理任何令人困惑的事情。

2 个答案:

答案 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