需要基于另一个查询中的选择结果的SQL Update语句。不知道如何将两者结合起来

时间:2012-07-10 16:57:43

标签: sql sql-server-2008

我的第一个选择陈述是:

 Select AL.asset_key, AL.group_key, AL.entity_key
From assetlist As AL
Where Not Exists    (
                Select 1
                From assetgroup As AG
                Where AG.group_key = AL.group_key
                    And AG.entity_key = AL.entity_key
                )

此查询的结果显示任何未正确分配group_key的资产。我们有一个错误,它抓住了第一个基于非独特字段的记录。因此,如果我有两个名为“Group 1”的组但存在于不同的父类别中,那么根据“Group 1”的第一个匹配编写新记录而不检查它是否是该类别的组的脚本。无论如何,它固定,所以我试图做一个清理脚本。上面的查询结果给了我这个:

 asset_key  group_key   entity_key
 352        25          3
 376        77          3
 378        77          3

我的另一个查询是检查group_key与实体密钥,以查看正确的值应该是什么:

SELECT distinct
t1.Group_key, t1.entity_key, t1.group_id
FROM assetgroup t1 
INNER JOIN assetgroup t2 ON t2.Group_ID = t1.Group_ID 
WHERE t2.Group_key != t1.Group_key
and t2.entity_key != t1.entity_key
and t2.Group_ID = t1.Group_ID
order by t1.group_id   

结果集如下所示:

Group_key   entity_key  group_id
25          1           CID
58          2           CID
59          3           CID
77          2           GROUP 1
79          3           GROUP 1
29          4           RENTAL
51          6           RENTAL
53          1           WAREHOUSE
36          5           WAREHOUSE

所以看两个结果我们看到group_key 25不属于entity_key 3,所以我需要更新资产。 group_key为正确的group_key值59,因为这是group_id CID中实体3的正确值。其他行也是如此。

我们有多个客户数据库都需要运行这个脚本,所以我不能轻松做到这一点,只看这两个并编写一些手动更新。如何编写此脚本以完成所有操作?

以下是对数据的更详细说明以及需要完成的工作:

  • 资产组表将每个资产链接到group_id,完全由group_key确定,group_key在表格中是唯一的。
  • 每个assetgroup group_key都与entity_key相关联。每个entity_key只在每个group_id中出现一次。
  • 在资产表中,列出了group_key和entity_key,但group_key应该完全暗示entity_key。
  • 但是,某些资产行的group_key错误,需要在与错误的group_key相同的group_id中更新为该实体的正确group_key。

3 个答案:

答案 0 :(得分:3)

以下是您正在寻找的查询。

UPDATE A
SET A.group_key = R.group_key
FROM
   Assets A
   INNER JOIN AssetGroup W -- Wrong
      ON A.group_key = W.group_key -- find group_id of wrong group_key
   INNER JOIN AssetGroup R -- Right
      ON A.entity_key = R.entity_key -- also W.entity_key = R.entity_key
      AND R.group_id = W.group_id -- wrong group_key still has right group_id
WHERE
   A.group_key <> R.group_key

请注意,您的数据库表是非规范化的,因为entity_key完全隐含group_key,但group_key在assets表中重复。这是一种反对行业最佳实践的数据库设计反模式。 group_key根本不应该在资产表中。至少,虽然不理想,但应该有一个约束来防止输入错误的值组合,例如从资产表到(group_key, entity_key)上的资产组表的FK关系。这有其自身的问题,例如无法在组之间移动实体。该组不唯一地标识实体,因此它实际上不是密钥的一部分,应该被删除。

最终,您的group_keys是使用单个值替换每个(entity_key, group_id)的代理。但是,然后重复资产表中的entity_key。这毫无意义。如果您的姓名为Joe Public,并且您获得了一个唯一引用此全名的号码,例如28975,那么为什么您将自己称为Joe 28975?无论是一个还是另一个。这个问题就是你可以说像Moe 28975之类的东西根本没有提到任何人!然后,您必须查找人员编号28975的真实名字,并将Moe更改为Joe。这不是最佳的。

在您的情况下,已损坏的流程仅根据姓氏在表格中查找Public并提取相应的号码11645,并提供Joe 11645,这是另一个无效组合:

Last   First Number
------ ----- ------
Public Joe   29875
Public Moe   11645

因此,您必须查找11645的姓氏,并找到Joe的正确数字。我希望这有助于说明这个方案是如何混淆的。

我还想温和地反对表格中的列顺序。一般来说,至少对我而言,将父列放在子列之前是最有意义的。将子列放在第一位是令人困惑的(特别是没有解释)。并且后缀id几乎普遍用于数据库中以表示内部标识符。看到group_id并意识到它是文本字符串而不是数字有点令人不安。实际上,这是另一种非规范化。如果您要将group_id WAREHOUSE重命名为WAREHOUSE 1,因为您要添加WAREHOUSE 2,该怎么办?现在,您必须更新引用它的所有行中的值。很明显,group_ids被人类使用并且本身具有重要意义,所以它们不应该在很多行中反复重复。

答案 1 :(得分:1)

我认为这样的事情会起作用(我自己也不能自己检查)。

我强烈建议您在尝试之前将其包装在一个事务中:

    with correctGroup(groupKey, entityKey) as (
        SELECT distinct t1.Group_key, t1.entity_key
        FROM assetgroup t1 INNER JOIN assetgroup t2 ON t2.Group_ID = t1.Group_ID 
        WHERE t2.Group_key != t1.Group_key
            and t2.entity_key != t1.entity_key
            and t2.Group_ID = t1.Group_ID)
    update  assetList
    from    assetList al join correctGroup cg on al.entity_key = cg.entitykey
    set     group_key = groupKey
    Where Not Exists (
                    Select 1
                    From assetgroup As AG
                    Where AG.group_key = AL.group_key
                        And AG.entity_key = AL.entity_key
                    )

答案 2 :(得分:0)

好的,所以我的一位同事在嘲笑我过于复杂的努力后提出了解决方案。这是查询:

enter image description here

很抱歉没有详细介绍这背后的业务逻辑。当时我并不认为这是相关的,但我发现现在是这样。欢呼所有,感谢您的投入和忍受一个蓬松的开发人员:)

哦,我的工作防火墙突然认为我正在尝试sql注入攻击,因此图像而不是代码。