7

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

如果您熟悉 SOLID 原则,请练习 TDD 并对您的工作进行单元测试,请勿继续阅读。除非你想对某人的不幸大笑,并为自己的伟大而幸灾乐祸,因为你知道你永远不会为你的继任者留下如此巨大的一堆废话。

所以,如果你坐得舒服,那我就开始。

在过去 7 个月里我继承并支持和修复错误的这个应用程序中,一个 6 个半月前离开的开发人员给我留下了一个令人毛骨悚然的问题。是的,在我开始后 2 周。

反正。在这个应用程序中,我们有clients,employeesvisits表。

还有一个称为AppNewRef(或类似名称)的表...等待它...包含用于其他每个表的下一个记录 ID。因此,可能包含以下数据:-

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

当应用程序为 创建新行时Employees,它会在AppNewRef表中查找,获取值,使用该值作为 ID,然后更新NextRef列。ClientsVisits所有其他要使用的表都NextID存储在这里。

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

因此,我们的用户非常高兴地创建Employees,ClientsVisitsVisits ID每次稳定增加几十个。然后问题就发生了。我们的客户端在创建批量访问时导致数据库损坏,因为服务器正在很好地运行,并且应用程序变得无响应。所以他们使用任务管理器杀死应用程序,而不是耐心等待。当然,该应用程序似乎确实被锁定了。

继续到今年早些时候,开发人员 Tim(真实姓名。此处不保护有罪者)开始修改代码以分阶段进行批量更新,以便 UI 保持“响应式”。然后四月来了,他正在处理他的通知(你现在可以想象这个场景,不是吗?)他正在忙着完成更新。

4 月底和 5 月初,我们更新了一些客户。在接下来的几个月中,我们会更新越来越多的内容。

在蒂姆(真名,记住)和我(蒂姆离开前两周开始)以及一周后开始的另一位新开发人员看不见的情况下,访问表中的 ID 开始大幅上升。巨大的,我的意思是一次10000、20000、30000。有时几十万。

下面的图表说明了使用的 ID 的快速增长。

看看他的图表

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

我不认为让代码处理任何长于 ID 的东西是正确的方法,因为这个应用程序将该 ID 传递到其他 DLL 和 OCX 并破坏那些接口似乎是一个我没有伤害的整个世界不想现在遇到。

我正在调查的一个潜在想法是尝试修改 ID,以便我可以将它们降低到较低级别。基本上填补了空白。使用ROW_NUMBER功能

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

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

想法?建议?T-SQL 的片段帮助大家感激不尽。

4

2 回答 2

2

选项一:

显式约束所有外键关系,并将它们设置为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

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

这是一个非常可怕的选择。如果您忘记更新表,您只是破坏了数据。但是,您可以给该temp表起一个更好听的名称并保留它。


祝你好运。愿主怜悯你的灵魂。如果你在黑暗的小巷里遇见他,蒂姆也是。

于 2012-11-14T10:20:59.973 回答
1

我会创建一个数字表,它只有一个从 1 到任何最大值的序列,增量为 1 很长,然后更改获取 visitid 的最大值的逻辑,也许其他人在数字和访问表之间进行正确连接. 然后你可以只寻找那个数字的 te min

select min(number) from visits right join numbers on visits.id = numbers.number

这样,您无需更改任何其他表格即可填补所有空白。

但我只想重做整个数据库。

于 2012-11-14T10:15:03.903 回答