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

T-SQL“合并”两行,或“重新设置”所有FK关系,sql,sql-server,tsql,Sql,Sql Server,Tsql,我有一个生产数据库,其中偶尔需要合并单个表中的冗余行 让我们假设这个表中的两行都有相同的值,除了它们的ID Table "PrimaryStuff" ID | SomeValue 1 | "I have value" 2 | "I have value" 3 | "I am different" 我们还假设存在许多相关的表。因为重复项是在PrimaryStuff表中创建的,所以通常在这些子表中创建的行都应该与PrimaryStuff表上的单个记录相关。这些表的数量和名称不在我的控制之下,

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

让我们假设这个表中的两行都有相同的值,除了它们的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更改为1s,然后删除PrimaryStuff的第2行。这应该很简单,就像PrimaryStuff的第1行不存在一样,我可以将第2行的主键更新为第1行,这样更改就会层叠而出。我不能这样做,因为这将是PrimaryStuff唯一索引中的重复键


请随意提问,我会尽力澄清任何令人困惑的问题。

首先让我们获得需要更新的行列表,据我所知,您希望最低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
现在我们有一个变更列表。我们可以在update语句中使用它,将它放在临时表或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

注意-未测试的代码可能有拼写错误。

您是否可以更主动地使用现有ID(当SomeValue已经存在时)并对PrimaryStuff.SomeValue强制执行唯一约束,或者为什么不将SomeValue作为PrimaryStuff的主键。使用它作为PrimaryKey,只有当PrimaryStuff中不存在某个值时,才能将记录添加到PrimaryStuff中

最后,也是最简单的一点,如果某个值总是被其他人任意定义,而你拿走了他们给你的任何东西,为什么不干脆放弃PrimaryStup,让用户在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

谢谢你的回复,霍根。你走上正轨了。不过,你确实错过了美中不足的机会,这是一个棘手的问题:我如何处理n个ForeignStuff表,其中n表示未知数量的表,如ForeignStuff,其中包含PrimarySuff表的外键。@OliverKane-只需为每个表运行脚本,或者,如果您需要使用上述内容作为模板自动编写动态SQL。在这种情况下,您可能希望使用临时表而不是CTE来提高多个查询的性能。@OliverKane-如果您不理解,在我修改ForeignStuff表之前,我的解决方案并不关心这些表——在此之前,我只是准备好了。事实上,我刚刚根据您的工作拼凑出一个存储过程来完成这项工作。答案授予您,好心的先生。@FélixGagnon Grenier-对。下面是一些不严肃的SQL->将MONKEY插入桶中,其中FUNNewId的映射。不过这是正确的。寻找更接近霍根发布的东西。另外,请记住,ForeignStuff*名称在查询的设计时是未知的。这是一匹不同颜色的马。我建议您使用nosql,或者在关系数据库的约束范围内尽可能地模仿它,例如,忘记primarystuff表,因为它是偶然的,在您描述的情况下不需要它……但您认为关系数据库可能具有级联更新/删除行为,而它们没有。创建视图不起作用,但您可以在运行时在代码中生成类似的查询。很遗憾,我不能选择NoSQL,尽管我希望可以,因为我喜欢RavenDB。因此,如果我理解正确,即使我有一个表示用户的主表,但这是一个不正确的模型,因为实际的主表
是否在国外数据库中?我同意,尽管我不愿意把所有的内部关系都抛到这张桌子上。除了这个用例之外,它的行为就像是本地的一样。此外,如果您还没有,您可能希望查找DDD概念,如上下文映射、有界上下文、反腐败层、一致性,以便了解从其他输出到下游的权衡,以及什么可能最适合您的情况。