Sql 更新并更正具有复合主键的表

Sql 更新并更正具有复合主键的表,sql,sql-server-2014-express,Sql,Sql Server 2014 Express,我在数据库中有一个表,用于记录特定场所的设备项目。它有一个复合主键,该主键由前提id的int形式的id组成,然后是一个单独的数字(在本例中是一个十进制数字),该数字为每个单独的设备添加 虽然这种设置在开始时工作良好,但在几年后,随着设备位从计划中添加或删除,会为复合密钥的第二部分生成一组连续的数字,它开始类似于下面的示例 我的问题是双重的。首先,是否存在可以针对复合密钥的第二部分编写的查询,该查询将向其恢复一组连续的数字?例如,简单地将38.3替换为39将立即产生与编号为39的现有记录的冲突。我

我在数据库中有一个表,用于记录特定场所的设备项目。它有一个复合主键,该主键由前提id的int形式的id组成,然后是一个单独的数字(在本例中是一个十进制数字),该数字为每个单独的设备添加

虽然这种设置在开始时工作良好,但在几年后,随着设备位从计划中添加或删除,会为复合密钥的第二部分生成一组连续的数字,它开始类似于下面的示例

我的问题是双重的。首先,是否存在可以针对复合密钥的第二部分编写的查询,该查询将向其恢复一组连续的数字?例如,简单地将38.3替换为39将立即产生与编号为39的现有记录的冲突。我假设实现这一点的方法可能是通过某种计数,然后向后遍历表,尽管我怀疑即使这样做也可能导致冲突,从而导致更新失败


其次,也许更重要的是,这是多年前设计的。我确信有更好的方法来构建这样一个表,因此我欢迎针对这种类型的场景提出一些“最佳实践”想法。

假设以下表结构

CREATE TABLE foo (id_1 int NOT NULL, id_2 decimal NOT NULL, data text NOT NULL);
行号

您可以使用ROW\u NUMBER生成一个有序的id\u 2 int

ROW_NUMBER函数将开始按id_2 order BY的升序对具有相同id_1分区BY的每组行进行编号。 你写的id_1,id_2是一个键,所以它是唯一的。那你就不需要打平手了。如果您需要一个平局断路器,因为id_1、id_2不是唯一的,那么您可以在OVER子句中向ORDER BY添加另一列

无数据的示例结果:

id_1 | id_2 | ROW_NUMBER  
------------------------  
   1 |  1.1 |          1  
   1 |  1.2 |          2  
   1 |  2.1 |          3  
   1 |  5.5 |          4  
   2 |    1 |          1  
   2 |  3.3 |          2  
所以这里id_1,id_2和id_1,ROW_NUMBER都是标识行的唯一键

将小数相乘

假设小数点后面只有一个小数点。 然后你可以将它们全部乘以10,然后将它们转换为int。 这样,您将在这些数据和以前的数据之间保持密切的关系,这可能是出于历史原因

id_1 | id_2 | id_2 * 10 :: int  
------------------------  
   1 |  1.1 |         11  
   1 |  1.2 |         12  
   1 |  2.1 |         21  
   1 |  5.5 |         55  
   2 |    1 |         10  
   2 |  3.3 |         33  
可以将乘法列保留为新键的一部分,将旧id_2十进制列保留为不相关的仅数据列

**将id_2拆分为两列**

这是行编号方法的一种变体,它将id_2向下舍入,并将行编号用作唯一键id_1、Floorrid_2、行编号的第三列

这会混淆小数的小数部分

在小数点处将小数拆分为两个整数

这种方法将十进制数1.2拆分为两个整数1,2。 然后,您可以将id_1、楼层id_2、id_2*10%10设置为唯一键

SELECT id_1,  
       id_2,  -- keep if needed  
       floor( id_2 ) AS "id_2_new",  
       int( id_2 * 10 % 10 ) AS "id_3",  -- and cast to int
       data  
FROM foo;  

id_1 | id_2 | id_2_new | id_3  
-----------------------------  
   1 |  1.1 |        1 | 1  
   1 |  1.2 |        1 | 2  
   1 |  2.1 |        2 | 1  
   1 |  5.5 |        5 | 5  
   2 |    1 |        1 | 0  
   2 |  3.3 |        3 | 3  
这种方法允许您随时使用

SELECT id_1,  
       double( id_2 + id_3 / 10 ) AS "id_2_old"  
FROM   foo;  

设备的项目顺序对您来说似乎非常重要。为什么会这样?对不起,我在问,但通常零件的顺序并不重要。椅子由腿、座椅和靠背组成;“为什么我要以特定的顺序提及它们?”ThorstenKettner个人认为我一点也不感兴趣,但从这个表格中可以得到一些报告。因此,想象一下一座包含数百个项目的建筑,清单既是一个库存,也是一个计划,允许工程师四处走动并为其提供服务,如果考虑到这一点,能够整理清单是有意义的。嗯,但是,为什么要订购?如果我想对项目进行分类,例如每个房间,我会有另一个列房间,这样我既可以打印字母列表,也可以打印每个房间的列表。我想说的是:我通常可以根据条目的属性对它们进行排序。也有例外情况,例如发票中的位置,但这些通常是一次性写的东西。我将永远不必在两个现有位置之间插入发票位置。所以问题是:你有所有的属性来应用不同的顺序吗?你可以很容易地创建一个新的列位置,并通过按设备划分的行号来填充它。以后每当你需要插入一个新的位置时,你都会更新,比如,更新mytable set position=position+1,其中position>=@new\u position.@DomSinclair如果您也能将正确答案标记为已接受,那就太好了。那么,它会给我们两个人打分。谢谢你的建议。我刚刚用我很快创建的一个虚拟数据库尝试了这一点。毫无疑问,您的原始查询是有效的,因为它生成了一个有序列表,但必须遵守真正的警告,即只使用小数点后一位。一旦你有两到三个,它就会变得有点笨拙。我坦然承认,这场混乱是我自己造成的,而我所知甚少。我找到了4种不同的方法。我希望一个适合你想要的。也许你可以去w
第四种方法是在小数点处将小数拆分为两个整数。此外,我们都在这里学习:如果我犯了错误也没关系,只要我从错误中吸取教训。我正在测试数据库中创建新表,使其更接近我所拥有的表,然后使用第三种和第四种方法。与此同时,我已经记下了你的答案。谢谢。如果你也因为答案正确和有用而接受,那么我们都会得到分数。或者,如果您愿意,请稍后再来获取您的分数,以便为正确答案打分:。在我看来,这些小数的根本问题是,它们在一列中包含两个不同的信息部分。帐户余额的小数是可以的。作为部门+房间标识一部分的小数不可用。出于历史原因,您也可以将旧id_2列保留为文本。对于DB归一化,考虑起动器。
SELECT id_1,  
       id_2,  -- keep if needed  
       floor( id_2 ) AS "id_2_new",  
       int( id_2 * 10 % 10 ) AS "id_3",  -- and cast to int
       data  
FROM foo;  

id_1 | id_2 | id_2_new | id_3  
-----------------------------  
   1 |  1.1 |        1 | 1  
   1 |  1.2 |        1 | 2  
   1 |  2.1 |        2 | 1  
   1 |  5.5 |        5 | 5  
   2 |    1 |        1 | 0  
   2 |  3.3 |        3 | 3  
SELECT id_1,  
       double( id_2 + id_3 / 10 ) AS "id_2_old"  
FROM   foo;