【问题标题】:Update a column in Table B with multiple row values from Table A using XML PATH使用 XML PATH 使用表 A 中的多个行值更新表 B 中的列
【发布时间】:2019-09-05 19:28:35
【问题描述】:

我在表 A 中有 4 列,即 Inv_Num1、Inv_Date1、Inv_Amt1、Inv_DocNum1

我在表 B 中有 4 列,即 Inv_Num2、Inv_Date2、Inv_Amt2、Inv_Status2

我想通过使用条件为的内连接来匹配表 A 和表 B 之间的行

Invoice_Num1=Invoice_Num2  AND Invoice_Date1=Invoice_Date2  AND
Invoice_Amt1=Invoice_Amt2

当我进行这种匹配时,我可能会在表格中得到超过 1 行的结果

A (Invoice_DocNum1 column)

我尝试了 XML Path 代码,但我不知道如何在 Update 语句中实现

update cis2
set cis2.Inv_Status2 = 
(SELECT     
    TypeName = STUFF((
        SELECT '; ' + imd1.Inv_DocNum1
        FROM [VRS].[Table_B] cis1
        INNER JOIN [Table_A] imd1 
            ON cis1.Inv_Num1 = imd1.Inv_Num2
        WHERE cis1.Inv_Num1 = imd1.Inv_Num2
        AND cis1.Inv_Date1 = imd1.Inv_Date2
        AND cis1.Inv_Amt1 = imd1.Inv_Amt2

        FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
) FROM  Table_B cis2

【问题讨论】:

  • 你为什么要这样做?您已经很好地规范化了数据,并且您试图通过将分隔的数据猛烈撞击到单个元组中来破坏它。这违反了 1NF。
  • 但是如何使用所需数据更新 Inv_Status2 列?有没有其他方法可以做到这一点?请注意,我正在使用存储过程来更新 Inv_Status2 中的一些通用值。但我也想显示 Inv_Status2 列,如预期输出所示。
  • 为什么需要这个?我认为你没有抓住重点。这并不简单,因为您所做的不是关系数据的工作方式。您需要首先捕获您在 cte 或类似文件中创建的定界威胁。然后用它来更新你的表格。
  • 感谢您的意见,您能帮我举个例子吗? WITH CTE AS (SELECT DISTINCT Inv_Num1,Inv_DocNum1 = STUFF((SELECT ';'+ LTRIM(STR(Invoice_DocNum)) FROM TableA imd WHERE imd.Invoice_Number = imd1.Invoice_Number FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, '') FROM TableA AS imd1 ) UPDATE cis SET cis.Inv_Status2=b.Invoice_DocNumber1 FROM TableB AS cis INNER JOIN CTE AS b ON cis.Inv_Num2 = b.Inv_Num1我也试过类似的方法,不知道是否正确:(
  • 尝试查看我已经发布的示例。然后问问自己你将如何使用该列。当您需要更新它时会发生什么?当你需要搜索它时会发生什么?

标签: sql sql-server for-xml-path


【解决方案1】:

对您的数据库执行此操作违反了良好做法,因为它违反了 1NF。但是,如果您一心想要这样做,您仍然可以这样做。沿着这些思路应该可以工作。

with myCte as
(
    SELECT Inv_Num1
        , TypeName = STUFF((
            SELECT '; ' + imd1.Inv_DocNum1
            FROM [VRS].[Table_B] cis1
            INNER JOIN [Table_A] imd1 
                ON cis1.Inv_Num1 = imd1.Inv_Num2
            WHERE cis1.Inv_Num1 = imd1.Inv_Num2
            AND cis1.Inv_Date1 = imd1.Inv_Date2
            AND cis1.Inv_Amt1 = imd1.Inv_Amt2
            FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
    from Table_A
    group by Inv_Num1
)

update tb
set Inv_Status2 = c.TypeName
from Table_B tb
join myCte c on c.Inv_Num1 = tb.Inv_Num2

【讨论】:

  • 对不起,它没有给出预期的输出。它为 TableB 中的所有行项目(Inv_Status2 列)提供类似 a123、b123、a456、b456、a789、b789、c789 的输出
  • 如果这没有产生您想要的输出,那么是时候发布一些示例数据了。不是图片,而是文本,因此其他人可以复制和粘贴它来测试他们的解决方案。但请仔细查看您的查询。您的 where 谓词需要在 Amount 等列上进行匹配,但在您的示例中,这些值在您的“组”中似乎不一致。
【解决方案2】:

答案分为两部分。首先,您需要为每行生成逗号分隔的列表。最好的方法是 STRING_AGG (https://docs.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-2017)

您需要将它与group by 一起使用,例如select ..., STRING_AGG(Inv_DocNum1, ',') group by ...,其中... 代表您的三个字段形成唯一键。

其次,需要使用update ... from语法,见https://docs.microsoft.com/en-us/sql/t-sql/queries/update-transact-sql?view=sql-server-2017#l-specifying-a-table-alias-as-the-target-object。在您的情况下,它将是 from 您的目标表加入您在第一步计算的结果集。

【讨论】:

    猜你喜欢
    • 2013-10-01
    • 2014-05-10
    • 1970-01-01
    • 2020-02-19
    • 2021-06-06
    • 1970-01-01
    • 2012-11-23
    • 2020-09-12
    • 1970-01-01
    相关资源
    最近更新 更多