微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在SQL Server数据库中的非IDENTITY类型字段中重新分配ID

警告:这个悲惨的故事包含代码气味和糟糕的设计决策以及技术债务的例子.

如果您熟悉SOLID原则,请练习TDD并对您的工作进行单元测试,请勿阅读.除非你想要对某人的不幸进行一次好的傻笑,并且在你自己很棒的事情中幸灾乐祸地知道你永远不会为你的继任者留下如此巨大的废话.

所以,如果你坐得舒服,我会开始.

在这个应用程序中,我已经继承并一直支持和修复过去7个月的错误,我被一个开发人员留下了6个半月前离开了.是的,我开始后2周.

无论如何.在这个应用程序中,我们有客户,员工和访问表.

还有一个名为AppNewRef(或类似的东西)的表……等待它……包含用于其他每个表的下一个记录ID.所以,可能包含如下数据: –

TypeID    Description     NextRef
   1      Employees       804
   2      Clients         1708
   3      Visits          56783

当应用程序为Employees创建新行时,它会在AppNewRef表中查找,获取值,使用该值作为ID,然后更新NextRef列.对于客户端,Visits以及其中使用的NextID存储在此处的所有其他表也是如此.

是的,我知道,此数据库上没有自动编号的IDENTITY列.所有这些都是“当它是一个Access应用程序”的借口.这些ID保存在(VB6)代码中.因此,可能有多达20亿1.47亿条记录.好吧,这似乎运作得相当好. (除了应用程序正在更新和处理锁定/更新等事实,而不是数据库)

因此,我们的用户非常乐意创建员工,客户,访问等.访问ID一直稳定增加几十个.然后问题就发生了.我们的客户在创建批量访问时会导致数据库损坏,因为服务器正在好好地丢失,并且应用程序变得没有响应.因此,他们使用任务管理器杀死应用程序,而不是耐心等待.虽然应用确实似乎锁定了.

转到今年早些时候,开发人员蒂姆(真名.没有在这里保护有罪)开始修改代码以分阶段进行批量更新,以便UI保持“响应”.然后四月来了,他正在做他的通知(你现在可以想象现场,不是吗?)他正在努力完成更新.

4月底,5月初,我们更新了一些客户.在接下来的几个月里,我们会越来越多地更新它们.

蒂姆(真名,记得)和我(在蒂姆离开前两周开始)以及一周后开始的另一位新开发者看不见,访问表中的ID开始向上跳跃.巨大的,我的意思是10000,20000,30000一次.有时几十万.

这是一个图表,说明了使用的ID的快速增长.

11月滚.客户致电技术支持并报告他收到了错误消息.我查看错误消息并询问数据库,以便我可以调试代码.我发现这个值太长了.我做了一些查询,提取信息,将其放入Excel并绘制图形.

我不认为使代码处理的时间长于ID,这是正确的方法,因为这个应用程序将该ID传递给其他DLL和OCX,并打破那些看起来像是一个整个世界的伤害我不喜欢我现在想要遇到.

我正在调查的一个潜在想法是尝试修改ID,以便我可以将它们降低到较低的水平.基本上填补了空白.使用ROW_NUMBER函数

我正在考虑做的是为每个表添加一个新列,这些表具有对这些访问ID的外键引用(不是正确的外键思维,这个数据库中不存在这些约束).这个新列可以存储访问ID的旧(当前)值(哦,只是为了混淆事物;在某些表上它被称为EventID,在某些表上它被称为VisitID).

然后,对于引用该VisitID的每个其他表,更新为新值.

想法?建议? T-sql的片段帮助所有人感激不尽.

解决方法

方案一:

显式约束所有外键关系,并将它们设置为ON UPDATE CASCADE.

这意味着无论何时更改ID,外键都会自动更新.

然后你就像这样运行……

WITH
  resequenced AS
(
  SELECT
    ROW_NUMBER() OVER (ORDER BY id) AS newID,*
  FROM
    yourTable
)
UPDATE
  resequenced
SET
  id = newID

我已经很久没有这样做了,所以我忘了它是否会因为两个记录具有相同的id值而导致更新中期出现问题.如果确实如此,你可以先做这样的事情……

UPDATE yourTable SET id = -id

方案二:

确保未明确定义任何外键关系.如果是,请注意他们丢弃并删除它们.

然后做一些像……

CREATE TABLE temp AS
  newID INT IDENTITY (1,1),oldID INT
)

INSERT INTO temp (oldID) SELECT id FROM yourTable

/* Do this once for the table you are re-identifiering              */
/* Repeat this for all fact tables holding that ID as a foreign key */
UPDATE
  factTable
SET
  foreignID = temp.newID
FROM
  temp
WHERE
  foreignID = temp.oldID

然后重新应用任何现有的外键关系.

这是一个非常可怕的选择.如果您忘记更新表格,则只需填写数据即可.但是,你可以给临时表一个更好的名字并保持它.

祝好运.愿主可以怜悯你的灵魂.如果你在黑暗的小巷里见过他,那就是蒂姆.

原文地址:https://www.jb51.cc/mssql/76684.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐