通过删除重复项将One-One关系修复为One-Many

时间:2017-12-19 22:01:01

标签: sql sql-server duplicates

错误

最初,Orgs和Servers之间存在一对一的关系,其中Server的密钥只是一个OrganizationId,这是一个非常糟糕的设计,因为业务逻辑已经改变,现在多个Orgs可以拥有相同的服务器。在我们进行更改之前,我们只为每个组织复制了服务器,因此多个组织将具有相同子域的服务器。以下是当前设置。

Current Structure

要求

首先,不幸的是,这有很多数据,所以用正确的模型删除整个数据库是不合适的。

我们想要做的是现在删除不同子域上的重复服务器,例如,如果Org1和Org2的Ser1和Ser2都带有子域" test",我们将使FK Org.Server_Id成为具有该域的服务器的最低出现次数,在本例中为Ser1,因此对于Org1和Org2,它们的服务器将是Ser1。以下是一个高科技excel示例:

What we want the query to do

我们尝试过的事情

我们能够通过以下方式获得Org.Server_Id为基于Server.OrganizationId的正确值:

UPDATE Organization 
SET Server_Id = t.Id
FROM(
    SELECT Id, OrganizationId
    FROM Server
) t
WHERE t.OrganizationId = Organization.Id

但是每当我们尝试更进一步时,我们就会陷入困境,因为我们无法在内部FROM中使用ORDER BY来尝试以某种聚合方式获取第一个匹配项。

这是我们最终得到的,但当然它不起作用,因为我们无法进入内部,我也不认为这甚至是正确的道路:

UPDATE Organization SET Server_Id = t.Id  
FROM  
(  
    SELECT Id, OrganizationId
    FROM (
        SELECT TOP(1) Subdomain, Id, OrganizationId
        FROM Server
        WHERE Subdomain = t.Subdomain
    ) a
) t  
WHERE t.OrganizationId = Organization.Id

2 个答案:

答案 0 :(得分:1)

我不能说我完全理解你所做的一切,但在过去当我需要获取重复信息的第一个条目时,我在内部查询中使用了分区函数。我不知道你想如何订购结果,但它看起来像这样:

 (
 SELECT ROW_NUMBER() OVER (PARTITION BY column1, column2, etc... ORDER BY columnX DESC/ASC) As row_num, Id, OrganizationId
 FROM Server
 ) t
 WHERE t.OrganizationId = Organization.Id AND row_num = 1

这与你在第二个代码块中尝试做的事情基本相同(我相信)。 column1和column2将是您要折叠到一个条目中的重复数据列的集合,columnX将是按顺序排序结果的列。通过在WHERE语句中使用row_num = 1,您只能从内部查询中获取每个唯一column1,column2等的第一个结果。

答案 1 :(得分:0)

使用@ user2731076建议的分区后,我们可以将查询修改为:

UPDATE Organization SET Server_Id = t.Id  
FROM  
(
    SELECT ROW_NUMBER() OVER (PARTITION BY Subdomain ORDER BY [Server].Id ASC) As row_num, [Server].Id, OrganizationId, Subdomain
    FROM Organization
    INNER JOIN [Server] ON [Server].OrganizationId = [Organization].Id
) t
WHERE t.Subdomain IN(SELECT Subdomain FROM Server WHERE OrganizationId = Organization.Id) AND row_num = 1

我们对代码的问题:

t.OrganizationId = Organization.Id

是因为总有一个服务器与一个Org相关联,所以它只是将Org.Server_Id的值设置为它已经设置的值。所以我们想要找到的第一个实例是具有类似于当前Org服务器的子域的子域的服务器的row_num = 1。这需要Inner Join从分区中获取它,并通过WHERE子句中的IN语句从当前组织中获取它,因此我们可以为我们的组织执行t.Subdomain = subdomain。

这可能是一种更有效的方法,我们将在未来对其进行研究。