【问题标题】:SSIS/SQL Server XMLSSIS/SQL 服务器 XML
【发布时间】:2017-05-02 17:04:46
【问题描述】:

我正在从 SQL Server 2008R2 创建 XML 输出。在我想要的xml结构下面。 (我会尽量说清楚,但如果您需要更多信息,请告诉我) 我遇到的问题是节点“RecordId”。该字段必须是整个 XML 中的一个运行序列,无论它位于哪个节点下。即,任何类别下节点“RecordId”的每次出现都将比前一个节点多 1 个值,即使它属于不同的类别。

我的主要限制是,我必须严格使用 T-SQL,仅 SSIS(可以使用 VB 或 C# 执行脚本任务)。

<Root>
  <includedFileHeader>
    <GenDatetime>2017-01-13T11:53:36</GenDatetime>
    <OFtype>PD</OFtype>
    <issuerID>ABCDE</issuerID>
    **<RecordId>1</RecordId>**
  </includedFileHeader>
  <includedIssuerResult>
    <issuerId>ABCDE</issuerId>
    **<RecordId>2</RecordId>**
    <includedPlanResult>
      <planId>2</planId>
      <insPlanId>123456789</insPlanId>
      **<RecordId>4</RecordId>**
      <ClassStatusType>
        <Code>A</Code>
      </ClassStatusType>
      <includedDetails>
        <DetailId>48</DetailId>
        <DetailClmId>A3456H567</DetailClmId>
        **<RecordId>4</RecordId>**
      </includedDetails>
      <includedDetails>
        <DetailId>74</DetailId>
        <DetailClmId>163364170257204</DetailClmId>
        **<RecordId>5</RecordId>**
      </includedDetails>
    </includedPlanResult>
    <includedPlanResult>
      <planId>3</planId>
      <insPlanId>343546337</insPlanId>
      **<RecordId>6</RecordId>**
      <ClassStatusType>
        <Code>A</Code>
      </ClassStatusType>
      <includedDetails>
        <DetailId>55</DetailId>
        <DetailClmId>A78947J780</DetailClmId>
        **<RecordId>7</RecordId>**
      </includedDetails>
      <includedDetails>
        <DetailId>44</DetailId>
        <DetailClmId>146545165A54</DetailClmId>
        **<RecordId>8</RecordId>**
      </includedDetails>
  </includedIssuerResult>
</Root>

我什至无法在 T-sql 中实现关闭,但尝试在脚本任务中使用一些 VB/C# 代码,使用 XMLReader、Streamreader/writer,但没有任何效果。非常感谢您提供任何帮助。

【问题讨论】:

  • 由于 XML 结构中的节点与位置无关,我将通过 (1) 构建基本 XML 结构,然后 (2) 使用脚本扫描结构的文本以查找任何开口需要 RecordId 值的块的标签(&lt;included**&gt; 标签),并将这些标签替换为由原始标签 + &lt;RecordId&gt;##&lt;/RecordId&gt; 组成的字符串。这应该完全符合您的需要。
  • 显示您目前尝试过的代码

标签: c# sql-server xml vb.net ssis


【解决方案1】:

一般来说,我会避免在字符串级别处理 XML。在这种情况下,这可能是一种解决方法...

DECLARE @xml XML=
N'<Root>
  <includedFileHeader>
    <GenDatetime>2017-01-13T11:53:36</GenDatetime>
    <OFtype>PD</OFtype>
    <issuerID>ABCDE</issuerID>
    <RecordId>99</RecordId>
  </includedFileHeader>
  <includedIssuerResult>
    <issuerId>ABCDE</issuerId>
    <RecordId>33</RecordId>
    <includedPlanResult>
      <planId>2</planId>
      <insPlanId>123456789</insPlanId>
      <RecordId>22</RecordId>
      <ClassStatusType>
        <Code>A</Code>
      </ClassStatusType>
      <includedDetails>
        <DetailId>48</DetailId>
        <DetailClmId>A3456H567</DetailClmId>
        <RecordId>66</RecordId>
      </includedDetails>
      <includedDetails>
        <DetailId>74</DetailId>
        <DetailClmId>163364170257204</DetailClmId>
        <RecordId>11</RecordId>
      </includedDetails>
    </includedPlanResult>
    <includedPlanResult>
      <planId>3</planId>
      <insPlanId>343546337</insPlanId>
      <RecordId>6</RecordId>
      <ClassStatusType>
        <Code>A</Code>
      </ClassStatusType>
      <includedDetails>
        <DetailId>55</DetailId>
        <DetailClmId>A78947J780</DetailClmId>
        <RecordId>7</RecordId>
      </includedDetails>
      <includedDetails>
        <DetailId>44</DetailId>
        <DetailClmId>146545165A54</DetailClmId>
        <RecordId>8</RecordId>
      </includedDetails>
    </includedPlanResult>
  </includedIssuerResult>
</Root>';

--查询

WITH Splitted AS
(
    SELECT CAST('<x>' + REPLACE((SELECT CAST(@xml AS NVARCHAR(MAX)) FOR XML PATH('')),'&lt;RecordId&gt;','</x><x>') + '</x>' AS XML) AS Part
)
,Parted AS
(
    SELECT p.value(N'(./text())[1]',N'nvarchar(max)') AS line
         ,ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1 AS nr 
    FROM Splitted
    CROSS APPLY Part.nodes(N'/x') AS A(p)
)
SELECT CAST(
        (SELECT line FROM Parted WHERE nr=0)
     +  (
        SELECT  '<RecordId>' + CAST(nr AS NVARCHAR(10)) + '</RecordId>' + SUBSTRING(line,CHARINDEX('<',line,5),10000)
        FROM Parted 
        WHERE nr>0
        ORDER BY nr     
        FOR XML PATH(''),TYPE 
        ).value('(./text())[1]',N'nvarchar(max)') AS XML);

简短说明

第一个 CTE Splitted 使用基于 XML 的 on-the-fly 字符串拆分方法。这里的问题是,像&lt;&gt; 这样的字符会让这个中断。这就是我在实际拆分之前使用SELECT ... FOR XML PATH('') 的原因。这将隐式转义所有禁止字符。下一步是在&amp;lt;RecordId&amp;gt; 处拆分字符串,现在是&amp;lt;RecordId&amp;gt;

第二个 CTE Parted 使用.nodes() 获取列表派生表,使用ROW_NUMBER 获取流水号。

最后的SELECT 重新连接分界线,在正确的位置插入编号为&amp;lt;RecordId&amp;gt;

【讨论】:

    【解决方案2】:

    在这里感谢大家的建议和帮助。我设法想出了一个解决方案(类似于 Laughing Vergil 的想法)(虽然不是那么整洁,但能够同时得到我想要的东西并不是很慢)。

    这就是我所做的: 我创建了一个标量 UDF,它将接受 2 个输入 1.XML 数据,2.我想要序列的标签的名称(在这种情况下,它是“RecordID”,只是为了在将来需要不同的标签时重用相同的标签)。 另外,我在生成 XML 时将“&lt;RecordID/&gt;”字段默认为 0,将在其中进行搜索和替换。

    函数的作用: 1. 将输入的 XML 转换为 VARCHAR(MAX) 2. 找到第一次出现的“&lt;RecordID/&gt;”,并用#1 作为序列填充它 3. 继续循环遍历 XML,直到找到最后一个匹配项,并为每个匹配项填充适当的循环编号 (@i)

    CREATE FUNCTION dbo.UDF_UPDATE_RECORDID_XML (
      --XML Data
      @xmld VARCHAR(MAX),
      --Tag/Node name for the RecordID
      --ex: if <RecordID>0</RecordID>, then pass 'RecordID'
      @rec_tag VARCHAR(100)
    )
    RETURNS XML
    AS
    BEGIN
    
    
      --o/p param
      DECLARE @op_xmld VARCHAR(MAX)
    
      --processing params
      DECLARE @i INT
      DECLARE @j INT
      DECLARE @prev_i INT
      --search pattern
      DECLARE @pat VARCHAR(100) = '<' + @rec_tag + '>0</' + @rec_tag + '>'
    
      SELECT @op_xmld = @xmld
    
      --Find first occurance of the pattern in the XML
      SELECT @i = PATINDEX('%' + @pat + '%', @op_xmld)
            , @j = 1
    
      SELECT @prev_i = @i
    
      --Loop until there is no match for the pattern
      WHILE (@i > 0)
      BEGIN
            --Replace the identified substring with the sequence number in between
            SET @op_xmld = STUFF(@op_xmld, @i, LEN(@pat), '<' + @rec_tag + '>' + CAST(@j AS VARCHAR(20)) + '</' + @rec_tag + '>')
    
            --sequence number
            SET @j = @j + 1
    
            --store the current index value of the pattern
            --so the next search can happen after this point
            SET @prev_i = @i
    
            --Search down for the pattern from the previous index value
            SELECT @i = CHARINDEX(@pat, @op_xmld, @prev_i + LEN(@pat))
      END
    
          RETURN CAST(@op_xmld AS XML)
    END
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-15
      • 1970-01-01
      • 2016-07-19
      • 2019-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-15
      相关资源
      最近更新 更多