【问题标题】:TSQL XML - Add attribute to parent nodeTSQL XML - 向父节点添加属性
【发布时间】:2023-12-08 07:59:01
【问题描述】:

我正在尝试向最外的父节点添加一个属性。附件是我的代码、我的结果和我的预期结果,以便更好地理解我正在尝试做的事情。非常感谢任何帮助!

我的代码:

        SELECT

         (
         SELECT
            (SELECT 
               'quoteRequest' AS '@service'
               FOR XML PATH('request'), TYPE),

        (SELECT
          (SELECT 
            'Add' as Action,
            'generation' AS 'Date/@type',
            'MM/DD/YYYY HH:MM' as Date
        FOR XML PATH(''), TYPE, ROOT ('Header')),

        (SELECT 
          (SELECT 
            '' AS Status, 
            'CustAcctNum' AS 'Enterprise/@customerAcctNum',
            'Name' AS 'Enterprise/@name',
            'Enterprise Name' AS 'Enterprise'
                FOR XML PATH(''), TYPE),
         (SELECT 
        'quoteNumber' AS 'ReferenceNumber/@type',
        'true' AS 'ReferenceNumber/@isPrimary',
        'VALUE' AS ReferenceNumber
        FOR XML PATH('ReferenceNumbers'),
        TYPE),

        (SELECT 
        'Special Instructions' AS 'Comment/@type',
        'Comment' AS Comment
        FOR XML PATH('Comments'),
        TYPE),

        (SELECT 
        'Forklift' AS 'EquipmentCode/@desc',
        '1' AS 'EquipmentCode/@qty',
        'DV' AS EquipmentCode
        FOR XML PATH('EquipmentList'),
        TYPE),

        (SELECT(
           SELECT(
             SELECT(
                SELECT
                  'earliest' AS '@type',
                  GETDATE()
                   FOR XML PATH('Date'), TYPE),
               (SELECT
                  'latest' AS '@type',
                  GETDATE()
                   FOR XML PATH('Date'), TYPE)
                FOR XML PATH('Pickup'),TYPE),
          (SELECT(
            SELECT(
              SELECT
                'earliest' AS '@type',
                GETDATE()
                 FOR XML PATH('Date'), TYPE),
             (SELECT
                'latest' AS '@type',
                GETDATE()
                 FOR XML PATH('Date'), TYPE)
               FOR XML PATH('Drop'),TYPE))
             FOR XML PATH('Dates'), TYPE)),

        (SELECT
          (SELECT
            (SELECT 
            'false' AS '@isresidential',
            '' AS 'Alias',
            'Name' AS 'Name',
            'AddrLine1' AS 'AddrLine1',
            'AddrLine2' AS 'AddrLine2',
            'City' AS 'City',
            'StateProvince' AS 'StateProvince',
            'PostalCode' AS 'PostalCode',
            'CountryCode' AS 'CountryCode',
            (SELECT
               'Name' AS 'Name',
                 (SELECT 
                    'phone' AS 'ContactMethod/@type',
                    '1' AS 'ContactMethod/@sequenceNum',
                    '8675309' AS 'ContactMethod'
                     FOR XML PATH('ContactMethods'), TYPE)
                FOR XML PATH('Contact'), TYPE, ROOT('Contacts')),
            (SELECT 
               'Comments' as 'Comments'
                FOR XML PATH (''), TYPE)

            FOR XML PATH('Address'), TYPE)

        FOR XML PATH(''), TYPE, ROOT('Shipper'))),

        (SELECT
           (SELECT

            (SELECT 
            'false' AS '@isresidential',
            '' AS 'Alias',
            'Name' AS 'Name',
            'AddrLine1' AS 'AddrLine1',
            'AddrLine2' AS 'AddrLine2',
            'City' AS 'City',
            'StateProvince' AS 'StateProvince',
            'PostalCode' AS 'PostalCode',
            'CountryCode' AS 'CountryCode',
            (SELECT
               'Name' AS 'Name',
                 (SELECT 
                    'phone' AS 'ContactMethod/@type',
                    '1' AS 'ContactMethod/@sequenceNum',
                    '8675309' AS 'ContactMethod'
                     FOR XML PATH('ContactMethods'), TYPE)
                FOR XML PATH('Contact'), TYPE, ROOT('Contacts')),
            (SELECT 
               'Comments' as 'Comments'
                FOR XML PATH (''), TYPE)

            FOR XML PATH('Address'), TYPE)

        FOR XML PATH(''), TYPE, ROOT('Consignee'))),
        (
        SELECT(

        SELECT(
            (SELECT 
            'false' AS '@stackable',
            '1' AS '@sequence',
            (SELECT 
               'UOM' AS 'Quantity/@units',
               '1' AS 'Quantity'
                FOR XML PATH(''), TYPE),
            (SELECT 
               'UOM' AS 'Weight/@units',
               '1' AS 'Weight'
                FOR XML PATH(''), TYPE),
                (SELECT 
                '1' AS '@height',   
                '1' AS '@units',   
                '1' AS '@width',
                '1' AS '@length'  
                FOR XML PATH('Dimensions'), TYPE),
                (SELECT
                'freghtClass' AS '@freightClass',
                '1' AS '@sequence',
                (SELECT 
                   'UOM' AS 'Weight/@units',
                   '1' AS 'Weight'
                    FOR XML PATH(''), TYPE),
                    (SELECT 
                    '1' AS '@height',   
                    '1' AS '@units',   
                    '1' AS '@width',
                    '1' AS '@length'  
                    FOR XML PATH('Dimensions'), TYPE),
                '1' AS 'Quantity'
                FOR XML PATH('Item'), TYPE, ROOT('Items'))

            FOR XML PATH('HandlingUnit'), TYPE))

          FOR XML PATH(''), TYPE, ROOT('HandlingUnits'))),

        (
        SELECT(
          SELECT 'Method' AS Method
             FOR XML PATH(''), TYPE),
            (SELECT 'true' AS '@thirdParty',

            (SELECT 
            'false' AS '@isresidential',
            '' AS 'Alias',
            'Name' AS 'Name',
            'AddrLine1' AS 'AddrLine1',
            'AddrLine2' AS 'AddrLine2',
            'City' AS 'City',
            'StateProvince' AS 'StateProvince',
            'PostalCode' AS 'PostalCode',
            'CountryCode' AS 'CountryCode',
            (SELECT
               'Name' AS 'Name',
                 (SELECT 
                    'phone' AS 'ContactMethod/@type',
                    '1' AS 'ContactMethod/@sequenceNum',
                    '8675309' AS 'ContactMethod'
                     FOR XML PATH('ContactMethods'), TYPE)
                FOR XML PATH('Contact'), TYPE, ROOT('Contacts'))

            FOR XML PATH('Address'), TYPE)
           FOR XML PATH('BillTo'), TYPE)

        FOR XML PATH(''), TYPE, ROOT('Payment'))

        FOR XML PATH(''), TYPE, ROOT('Shipment'))

        FOR XML PATH(''), TYPE, ROOT('QuoteRequest'))

        FOR XML PATH(''), TYPE, ROOT('request'))

我的结果。注意前两行:

      **<request>
        <request service="quoteRequest" />**
        <QuoteRequest>
          <Header>
            <Action>Add</Action>
            <Date type="generation">MM/DD/YYYY HH:MM</Date>
          </Header>
          <Shipment>
            <Status />
            <Enterprise customerAcctNum="CustAcctNum" name="Name">Enterprise Name</Enterprise>
            <ReferenceNumbers>
              <ReferenceNumber type="quoteNumber" isPrimary="true">VALUE</ReferenceNumber>
            </ReferenceNumbers>
            <Comments>
              <Comment type="Special Instructions">Comment</Comment>
            </Comments>
            <EquipmentList>
              <EquipmentCode desc="Forklift" qty="1">DV</EquipmentCode>
            </EquipmentList>
            <Dates>
              <Pickup>
                <Date type="earliest">2017-06-12T08:20:27.107</Date>
                <Date type="latest">2017-06-12T08:20:27.107</Date>
              </Pickup>
              <Drop>
                <Date type="earliest">2017-06-12T08:20:27.107</Date>
                <Date type="latest">2017-06-12T08:20:27.107</Date>
              </Drop>
            </Dates>
            <Shipper>
              <Address isresidential="false">
                <Alias />
                <Name>Name</Name>
                <AddrLine1>AddrLine1</AddrLine1>
                <AddrLine2>AddrLine2</AddrLine2>
                <City>City</City>
                <StateProvince>StateProvince</StateProvince>
                <PostalCode>PostalCode</PostalCode>
                <CountryCode>CountryCode</CountryCode>
                <Contacts>
                  <Contact>
                    <Name>Name</Name>
                    <ContactMethods>
                      <ContactMethod type="phone" sequenceNum="1">8675309</ContactMethod>
                    </ContactMethods>
                  </Contact>
                </Contacts>
                <Comments>Comments</Comments>
              </Address>
            </Shipper>
            <Consignee>
              <Address isresidential="false">
                <Alias />
                <Name>Name</Name>
                <AddrLine1>AddrLine1</AddrLine1>
                <AddrLine2>AddrLine2</AddrLine2>
                <City>City</City>
                <StateProvince>StateProvince</StateProvince>
                <PostalCode>PostalCode</PostalCode>
                <CountryCode>CountryCode</CountryCode>
                <Contacts>
                  <Contact>
                    <Name>Name</Name>
                    <ContactMethods>
                      <ContactMethod type="phone" sequenceNum="1">8675309</ContactMethod>
                    </ContactMethods>
                  </Contact>
                </Contacts>
                <Comments>Comments</Comments>
              </Address>
            </Consignee>
            <HandlingUnits>
              <HandlingUnit stackable="false" sequence="1">
                <Quantity units="UOM">1</Quantity>
                <Weight units="UOM">1</Weight>
                <Dimensions height="1" units="1" width="1" length="1" />
                <Items>
                  <Item freightClass="freghtClass" sequence="1">
                    <Weight units="UOM">1</Weight>
                    <Dimensions height="1" units="1" width="1" length="1" />
                    <Quantity>1</Quantity>
                  </Item>
                </Items>
              </HandlingUnit>
            </HandlingUnits>
            <Payment>
              <Method>Method</Method>
              <BillTo thirdParty="true">
                <Address isresidential="false">
                  <Alias />
                  <Name>Name</Name>
                  <AddrLine1>AddrLine1</AddrLine1>
                  <AddrLine2>AddrLine2</AddrLine2>
                  <City>City</City>
                  <StateProvince>StateProvince</StateProvince>
                  <PostalCode>PostalCode</PostalCode>
                  <CountryCode>CountryCode</CountryCode>
                  <Contacts>
                    <Contact>
                      <Name>Name</Name>
                      <ContactMethods>
                        <ContactMethod type="phone" sequenceNum="1">8675309</ContactMethod>
                      </ContactMethods>
                    </Contact>
                  </Contacts>
                </Address>
              </BillTo>
            </Payment>
          </Shipment>
        </QuoteRequest>
      </request>

我的预期结果,请注意第一行/节点是请求。与我上面得到的结果不同,我需要节点和属性在同一行:

      **<request service=quoteRequest">**
        <QuoteRequest>
          <Header>
            <Action>Add</Action>
            <Date type="generation">MM/DD/YYYY HH:MM</Date>
          </Header>
          <Shipment>
            <Status />
            <Enterprise customerAcctNum="CustAcctNum" name="Name">Enterprise Name</Enterprise>
            <ReferenceNumbers>
              <ReferenceNumber type="quoteNumber" isPrimary="true">VALUE</ReferenceNumber>
            </ReferenceNumbers>
            <Comments>
              <Comment type="Special Instructions">Comment</Comment>
            </Comments>
            <EquipmentList>
              <EquipmentCode desc="Forklift" qty="1">DV</EquipmentCode>
            </EquipmentList>
            <Dates>
              <Pickup>
                <Date type="earliest">2017-06-12T08:20:27.107</Date>
                <Date type="latest">2017-06-12T08:20:27.107</Date>
              </Pickup>
              <Drop>
                <Date type="earliest">2017-06-12T08:20:27.107</Date>
                <Date type="latest">2017-06-12T08:20:27.107</Date>
              </Drop>
            </Dates>
            <Shipper>
              <Address isresidential="false">
                <Alias />
                <Name>Name</Name>
                <AddrLine1>AddrLine1</AddrLine1>
                <AddrLine2>AddrLine2</AddrLine2>
                <City>City</City>
                <StateProvince>StateProvince</StateProvince>
                <PostalCode>PostalCode</PostalCode>
                <CountryCode>CountryCode</CountryCode>
                <Contacts>
                  <Contact>
                    <Name>Name</Name>
                    <ContactMethods>
                      <ContactMethod type="phone" sequenceNum="1">8675309</ContactMethod>
                    </ContactMethods>
                  </Contact>
                </Contacts>
                <Comments>Comments</Comments>
              </Address>
            </Shipper>
            <Consignee>
              <Address isresidential="false">
                <Alias />
                <Name>Name</Name>
                <AddrLine1>AddrLine1</AddrLine1>
                <AddrLine2>AddrLine2</AddrLine2>
                <City>City</City>
                <StateProvince>StateProvince</StateProvince>
                <PostalCode>PostalCode</PostalCode>
                <CountryCode>CountryCode</CountryCode>
                <Contacts>
                  <Contact>
                    <Name>Name</Name>
                    <ContactMethods>
                      <ContactMethod type="phone" sequenceNum="1">8675309</ContactMethod>
                    </ContactMethods>
                  </Contact>
                </Contacts>
                <Comments>Comments</Comments>
              </Address>
            </Consignee>
            <HandlingUnits>
              <HandlingUnit stackable="false" sequence="1">
                <Quantity units="UOM">1</Quantity>
                <Weight units="UOM">1</Weight>
                <Dimensions height="1" units="1" width="1" length="1" />
                <Items>
                  <Item freightClass="freghtClass" sequence="1">
                    <Weight units="UOM">1</Weight>
                    <Dimensions height="1" units="1" width="1" length="1" />
                    <Quantity>1</Quantity>
                  </Item>
                </Items>
              </HandlingUnit>
            </HandlingUnits>
            <Payment>
              <Method>Method</Method>
              <BillTo thirdParty="true">
                <Address isresidential="false">
                  <Alias />
                  <Name>Name</Name>
                  <AddrLine1>AddrLine1</AddrLine1>
                  <AddrLine2>AddrLine2</AddrLine2>
                  <City>City</City>
                  <StateProvince>StateProvince</StateProvince>
                  <PostalCode>PostalCode</PostalCode>
                  <CountryCode>CountryCode</CountryCode>
                  <Contacts>
                    <Contact>
                      <Name>Name</Name>
                      <ContactMethods>
                        <ContactMethod type="phone" sequenceNum="1">8675309</ContactMethod>
                      </ContactMethods>
                    </Contact>
                  </Contacts>
                </Address>
              </BillTo>
            </Payment>
          </Shipment>
        </QuoteRequest>
      </request>

我尝试了很多不同的方法,但仍然无法正常工作。有人有什么想法吗?非常感谢!

【问题讨论】:

    标签: sql sql-server xml tsql


    【解决方案1】:

    抱歉,但我想退后一步,质疑您为什么要一遍又一遍地让数据变得像选择一样复杂?如果它非常复杂并制作一个巨大的 tablix,我会将它分成可能的 Shipment、Shipper 等部分,但您应该能够使元素独立于任何树结构,然后根据需要读取它们。在模拟中,我可以展示我的意思。使用更明确的“XML Path()”,您可以有更多选项来创建人工节点,而不必仅仅为了创建节点名称而嵌套选择。例如:

    DECLARE @People TABLE (PersonId INT IDENTITY, PersonName VARCHAR(128));
    
    INSERT INTO @People (PersonName) VALUES ('Brett'), ('John'), ('Mark'), ('Shawn'), ('Ryan'), ('Kevin');
    
    DECLARE @XmlResults XML = 
    
    (
    SELECT 
      CAST(
        Replace(
        (SELECT 
          'quoteRequest' AS "@service"
        , 'Add' AS "QuoteRequest/Header/Action/*"
        , 'generation' AS "QuoteRequest/Header/Date/@type"
        , 'MM/DD/YYYY HH:MM' AS "QuoteRequest/Header/Date/*"
        , '' AS "QuoteRequest/Shipment/Status/*"
        , 'CustAcctNum' AS "QuoteRequest/Shipment/Enterprise/@customerAcctNum"
        , 'Name' AS "QuoteRequest/Shipment/Enterprise/@name"
        , PersonName AS "QuoteRequest/Shipment/Enterprise/*"
        , 'earliest' AS "QuoteRequest/Dates/Pickup/Date/@type"
        , FORMAT(GETDATE(), 'yyyy-MM-ddTHH:mm:ss.fff', 'en-US') AS "QuoteRequest/Dates/Pickup/Date/*"
        , 'latest' AS "QuoteRequest/Dates/Pickup/DateTwo/@type"
        , FORMAT(GETDATE(), 'yyyy-MM-ddTHH:mm:ss.fff', 'en-US') AS "QuoteRequest/Dates/Pickup/DateTwo/*"
        From @People
        FOR XML PATH('request'), ROOT('Requests'))
        , 'DateTwo', 'Date')
      AS XML)
    )
    
    --XML as is
    SELECT @XmlResults
    
    --XML parsed for one request at a time in my series.
    SELECT x.query('.')
    FROM @XmlResults.nodes('Requests/request') AS y(x)
    

    【讨论】:

    • 感谢您的回复。我之所以这样做,是因为这是我第一次使用 SQL 创建 XML。所以我并不精通最佳实践,不幸的是我没有时间做很多研究。您在模型中所做的看起来效果很好并保持性能健康。在定义属性后,您将如何使用您的方法创建未关闭的节点?像上面“Payment”节点中的“BillTo”和“Address”节点?
    • 这真的取决于获取数据的复杂程度。如果您可以在相当快的时间内为您的复杂数据快速返回一个 sql server 结果,而不是通过所有连接一次完成所有操作。否则将其与 CTE、表变量、嵌套选择等分开。您可以制作不同的节点树结构。在我的示例中,“Person”和“Orders”都是它们自己的节点结构。如果您从不从 SQL Server 获取数据,而只是随意制作 xml,那就更容易了。
    • 数据不会太难检索。可能要加入 2 或 3 张桌子。我计划在生成 XML 之前获取所有必要的数据,将它们设置为参数,然后在 XML 生成中引用这些参数。
    • 您是在做基于结果集的操作还是一次只做一个操作?不管你把它做得多么复杂,你都可以制作你的例子,而不是一次只做一件事。我为您的代码更新了我的示例,但假设我可以迭代数据。我没有做所有事情,因为您的 xml 结构很大。
    • 您推荐的效果很好。我遇到的一个问题是如何在我最初发布的预期结果中生成“日期”块。如您所见,“日期”在 Pickup 节点中被用作元素两次,而 SQL 不喜欢这样。不幸的是,XML 需要以这种方式发送。你有什么想法可以用你的方法解决这个问题吗?