从SQL Server生成单个XML文件,每个客户有多个结果

时间:2018-02-01 22:15:31

标签: sql-server xml tsql xpath

我需要将XML文件发送给供应商,其中包含来自SQL Server数据库的特定数据。我知道SQL Server确实与XML集成,但我不熟悉它,所以我决定只是选择XML标签作为连接文本,我需要的字段。这有效,但我意识到解决方案不能满足我们的需求。

原因在于,对于给定的客户,可能会有多个结果。如果有多个结果,由于SQL Server为每个结果返回一个单独的行,它也会为每个客户生成多个文件。我们需要为每个客户提供一个文件。

这是SQL的一部分,它会为每个客户生成多个结果:

-- Carriage return in variable for repeated use
declare @cr varchar(2) = char(13) + char(10)`

'<activity>' + @cr +
'<activity_date>' + CONVERT(VARCHAR(10), GETDATE(), 101) + '</activity_date>' + @cr +
'<activity_desc>' + Term.Code + ' Remaining Balance' + '</activity_desc>' + @cr +
'<charge>' + bal.Balance + '</charge>' + @cr + 
'</activity>'

我们需要完成的是如果有多个Term.Code字段,那么结果XML输出就是这样:

<activity> 
  <activity_date>02/01/2018</activity_date>
  <activity_desc>WINTER17D     Remaining Balance</activity_desc> 
  <charge>50.00</charge> 
</activity> 
<activity> 
  <activity_date>02/01/2018</activity_date> 
  <activity_desc>SPRING18A    Remaining Balance</activity_desc> 
  <charge>15.00</charge> 
</activity>
<activity> 
  <activity_date>02/01/2018</activity_date> 
  <activity_desc>SPRING18B   Remaining Balance</activity_desc> 
  <charge>20.00</charge> 
</activity>`

三个单独的<activity>标记,都在同一个文件中,但其余的XML每个客户只有一个标记。所以,完整的结果将是这样的:

<Billing> 
<stmt_date>02/01/2018</stmt_date> 
<id>987654</id> 
<total_due>85.00</total_due> 
<name>Test Person</name> 
<message>You have an outstanding balance.</message>
<activity> 
  <activity_date>02/01/2018</activity_date>
  <activity_desc>WINTER17D     Remaining Balance</activity_desc> 
  <charge>50.00</charge> 
</activity>
<activity> 
  <activity_date>02/01/2018</activity_date> 
  <activity_desc>SPRING18A    Remaining Balance</activity_desc> 
  <charge>15.00</charge> 
</activity>
<activity> 
  <activity_date>02/01/2018</activity_date> 
  <activity_desc>SPRING18B   Remaining Balance</activity_desc> 
  <charge>20.00</charge> 
</activity>
</Billing>

这可能吗?这是我们用来生成所有原始数据集的SQL查询:

SELECT  RTRIM(Person.PersonID) AS PersonID,
   CONVERT(VARCHAR(10), GETDATE(), 101) AS StatementDate,    
  bal.Balance,
  RTRIM(Person.StuNum) AS PersonNum,
  RTRIM(Person.Email) AS Email,
  Person.Addr1,
  Term.Code AS Term
FROM   Person(nolock)
  JOIN Enrollment(nolock)
    ON Person.PersonID = Enrollment.PersonID
  JOIN AcctStatus(nolock)
    ON Enrollment.EnrollmentID = AcctStatus.EnrollmentID
       AND AcctStatus.SaAcctStatusID NOT IN(5, 16)
  JOIN
(
   SELECT EnrollmentID,
        TermID,
        CAST(SUM(CASE Type
                   WHEN 'I'
                   THEN Amount
                   WHEN 'D'
                   THEN Amount
                   ELSE Amount * -1
                END) AS VARCHAR(10)) AS Balance
   FROM   Ledger
   GROUP BY EnrollmentID,
          TermID
) AS bal
    ON bal.EnrollmentID = Enrollment.EnrollmentID
       AND bal.Balance <> '0.00'
  JOIN Term(nolock)
    ON Term.TermID = bal.TermID
       AND Term.TermID NOT IN(105, 34, 60, 194, 193, 97, 74)
ORDER BY Person.PersonID,
    bal.TermID

2 个答案:

答案 0 :(得分:2)

有两个主要缺陷:

  • 永远不要通过字符串连接创建XML。试想一下,您的一个文本列包含'Do & Co'之类的数据。 &会破坏XML的有效性!
  • 绝不使用依赖于文化的日期格式!我不知道你的日期值是1月2日还是2月1日。始终使用安全且独立的格式,例如ISO8601(特别是在XML中)!

试试这样:

有一个模拟场景,你必须适应你的需求。基本的想法是使用FOR XML PATH()作为结算数据,使用另一个子选择 - 再次使用FOR XML PATH(),TYPE进行相关(嵌套)活动。

DECLARE @mockupBilling TABLE(StatementDate DATE, ID INT, Balance DECIMAL(10,4),Name VARCHAR(100),Msg VARCHAR(MAX));
INSERT INTO @mockupBilling VALUES(GETDATE(),987654,85.0,'Test Person','You have an outstanding balance.');

DECLARE @mockupActivity TABLE(BillingID INT, ActivityDate DATE, Activity VARCHAR(100), Charge DECIMAL(10,4));
INSERT INTO @mockupActivity VALUES
 (987654,{d'2018-01-02'},'WINTER17D Remaining Balance',50.0)
,(987654,{d'2018-01-03'},'SPRING18A Remaining Balance',15.0);

SELECT b.StatementDate AS [stmt_date]
      ,b.ID AS [id]
      ,b.Balance AS [total_due]
      ,b.Name AS [name]
      ,b.Msg AS [messge]
      ,(
        SELECT a.ActivityDate AS [activity_date]
              ,a.Activity AS [activity_desc]
              ,a.Charge AS [charge]
        FROM @mockupActivity AS a
        WHERE a.BillingID=b.ID
        ORDER BY a.ActivityDate
        FOR XML PATH('activity'),TYPE
       ) 
FROM @mockupBilling AS b
FOR XML PATH('Billing');

结果

<Billing>
  <stmt_date>2018-02-02</stmt_date>
  <id>987654</id>
  <total_due>85.0000</total_due>
  <name>Test Person</name>
  <messge>You have an outstanding balance.</messge>
  <activity>
    <activity_date>2018-01-02</activity_date>
    <activity_desc>WINTER17D Remaining Balance</activity_desc>
    <charge>50.0000</charge>
  </activity>
  <activity>
    <activity_date>2018-01-03</activity_date>
    <activity_desc>SPRING18A Remaining Balance</activity_desc>
    <charge>15.0000</charge>
  </activity>
</Billing>

答案 1 :(得分:0)

正确的答案是为您的查询使用常规的select语句,但在其中添加FOR XML PATH('ACTIVITY') 我很乐意帮助您,尽管您还没有提供完整的查询。添加它,然后我自己或其他人可以指导你。

  

在此处阅读FOR XMLhttps://docs.microsoft.com/en-us/sql/relational-databases/xml/for-xml-sql-server