【问题标题】:Updating a primary key with another column of scrambled unique values用另一列加扰的唯一值更新主键
【发布时间】:2016-08-25 21:56:26
【问题描述】:

免责声明:此更改通常对正确规范化的数据库没有用处,但我有商业原因。

我有一个带有数值主键的数据表。此键在多个其他表中用作外键引用。还有一列数值可以更新以反映所需的行顺序。 order 和 PK 列包含相同的数字,但按任一列排序表会打乱另一列。

我要做的是更新主键以遵循与 order 列相同的顺序,但 SSMS 给我错误“违反 PRIMARY KEY 约束'PK_Constraint'。无法在对象'tbl 中插入重复键'。重复的键值是。"

我的更新语句如下所示:

update tbl set tbl.key = tbl.order where tbl.key <> tbl.order

我已经知道如何更新其他表中的外键引用,所以我只需要知道在这种情况下如何更新键。

【问题讨论】:

  • SELECT COUNT(*), COUNT(DISTINCT [key]), COUNT(DISTINCT [Order]) FROM tbl 返回什么?它应该都是相同的数字。如果内部系统主键编号与外部最终用户排序属性不匹配,您为什么要关心?主键仅供内部系统使用
  • 在我的例子中,我得到了64, 64, 64 作为结果。将主键与排序属性保持相同的顺序对于维护将使用此键构建的其他表中可能存在的数千行数据非常有用。我正在努力避免未来的问题。
  • 我从数据库角度的建议:单独的排序属性是个好主意。一个坏主意是让两列具有相同的值,并且将真实世界的值附加到主键通常是不好的。我不确定您设想的未来问题是什么,但我建议您通过这样做来增加它们。抱歉,我对您的原始问题没有任何直接的想法,除非可以尝试不使用 WHERE
  • 此表定义事件中的步骤。还有另一个表定义了子步骤。两者都有一个内部序列 ID 和一个订单字段。子步骤表有一个对步骤表的 PK 的 FK 引用,作为其自己的 PK 的一部分。然后有另一个表跟踪事件的各个实例,每个实例仅在每个步骤/子步骤完成时添加行。如果每个表的 PK 都是有序的,则无需连接即可更轻松地查看每个实例的步骤/子步骤的顺序。配置设置一次,然后在事件开始发生后不会更改。
  • 通过“无需连接”语句,一切都变得清晰起来。在规范化的数据库中,连接是一件的事情,而不是一件坏事。也许创建一个视图可能会让您更轻松。出于性能原因或代码复杂性原因,您想避免连接吗?无论如何,我重申 - 从数据库的角度来看,你正在做的是一个非常糟糕的主意。

标签: sql-server tsql sql-update constraints primary-key


【解决方案1】:

检查以确保tbl.Order 中没有重复值。如果有,您必须先解决重复项,然后才能使用这些值更新 PK 列。

SELECT
    order,COUNT(order) as NumDupes
FROM tbl
GROUP BY order
HAVING COUNT(order) > 1

【讨论】:

  • 啊,我忘记检查了。
  • 好的,我刚查了一下,订单栏没有重复。
【解决方案2】:

我最终发现了足够多的问题,我可以使用游标解决这个问题。我将我的解决方案放在这里以供参考。如果有人想简化/修改它以使用基于集合的查询,我会接受这个答案。

步骤 1

使用来自this answer 的查询,我发现有一些订单/ID“链”的一端会导致与基于简单集合的update 重复:

with parents as 
(
  select 1 idx, ID, Order, Name from tbl where ID <> Order
  union all 
  select idx+1, p.ID, v.Order, p.Name from parents p inner join tbl v on p.Order = v.ID and idx < 100
)
select parents from (
select distinct parents from (
select *, parents = stuff
    ( ( select ', ' + cast(p.Order as varchar(100)) from parents p 
        where t.ID = p.ID for xml path('')
    ) , 1, 2, '') from parents t ) x ) y
order by len(parents) desc

第二步

我手动查看了结果集以找到以给定值结尾的最长行。然后我按照给定的顺序将一个链中的值放入一个临时表中:

create table #tmp (id int identity(1,1), val int)
insert into #tmp values <list of values>

第三步

接下来我用游标遍历临时表并分别更新每一行(和外键引用):

declare @val int
declare @old int
declare val cursor for select val from #tmp order by id desc
open val
fetch next from val into @val
while @@fetch_status = 0
begin
    set @old = (select ID from tbl where Order = @val)

    insert into tbl(ID, <other columns>)
    select @val, <other columns> from tbl where ID = @old

    update <other tables> set FK_ID = @val where FK_ID = @old

    delete from tbl where ID = @old

    fetch next from val into @val
end;close val; deallocate val;

第四步

我为每个“链”重复了第 2 步和第 3 步。最后,我的表的主键与 Order 字段的顺序相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-15
    • 1970-01-01
    • 2016-03-14
    • 2023-02-16
    • 1970-01-01
    • 2021-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多