Concatenate XML without type casting to string

2020-06-01 05:55发布

问题:

I have the following XML generated from various tables in my SQL SERVER database

<XMLData>
...
<Type>1</Type>
...
</XMLData>

AND

<XMLData>
...
<Type>2</Type>
...
</XMLData>

AND

<XMLData>
...
<Type>3</Type>
...
</XMLData>

The final output I need is single combined as follows:

<AllMyData>
    <XMLData>
        ...
        <Type>1</Type>
        ...
    </XMLData>
    <XMLData>
        ...
        <Type>2</Type>
        ...
    </XMLData>
    <XMLData>
        ...
        <Type>3</Type>
        ...
    </XMLData>
<AllMyData>

NOTE - all the independent elements that I am combining have the same tag name.

Thanks in advance for looking this up.

回答1:

I have the following XML generated from various tables in my SQL SERVER database

Depends on how you have it but if it is in a XML variable you can do like this.

declare @XML1 xml
declare @XML2 xml
declare @XML3 xml

set @XML1 = '<XMLData><Type>1</Type></XMLData>'
set @XML2 = '<XMLData><Type>2</Type></XMLData>'
set @XML3 = '<XMLData><Type>3</Type></XMLData>'

select @XML1, @XML2, @XML3 
for xml path('AllMyData')


回答2:

I can't comment but can answer so even though I think a comment is more appropriate, I'll expand on what rainabba answered above to add a bit more control. My .Net code needs to know the column name returned so I can't rely on auto-generated names but needed the very tip rainabba provided above otherwise.

This way, the xml can effectively be concatenated into a single row and the resulting column named. You could use this same approach to assign the results to an XML variable and return that from a PROC also.

SELECT (
 SELECT XmlData as [*]
 FROM
     (
     SELECT
         xmlResult AS [*]
     FROM
         @XmlRes
     WHERE
         xmlResult IS NOT NULL
     FOR XML PATH(''), TYPE
     ) as DATA(XmlData)
 FOR XML PATH('')
) as [someColumnName]


回答3:

If you use for xml type, you can combine the XML columns without casting them. For example:

select  *
from    (
        select  (
                select  1 as Type
                for xml path(''), type
                )
        union all
        select  (
                select  2 as Type
                for xml path(''), type
                )
        union all
        select  (
                select  3 as Type
                for xml path(''), type
                )
        ) as Data(XmlData)
for xml path(''), root('AllMyData'), type

This prints:

<AllMyData>
    <XmlData>
        <Type>1</Type>
    </XmlData>
    <XmlData>
        <Type>2</Type>
    </XmlData>
    <XmlData>
        <Type>3</Type>
    </XmlData>
</AllMyData>


回答4:

As an addendum to Mikael Eriksson's answer - If you have a process where you need to continually add nodes and then want to group that under a single node, this is one way to do it:

declare @XML1 XML
declare @XML2 XML
declare @XML3 XML
declare @XMLSummary XML

set @XML1 = '<XMLData><Type>1</Type></XMLData>'
set @XMLSummary = (SELECT @XMLSummary, @XML1 FOR XML PATH(''))

set @XML2 = '<XMLData><Type>2</Type></XMLData>'
set @XMLSummary = (SELECT @XMLSummary, @XML2 FOR XML PATH(''))

set @XML3 = '<XMLData><Type>3</Type></XMLData>'
set @XMLSummary = (SELECT @XMLSummary, @XML3 FOR XML PATH(''))


SELECT @XMLSummary FOR XML PATH('AllMyData')


回答5:

I needed to do the same but without knowing how many rows/variables were concerned and without extra schema added so here was my solution. Following this pattern, I can generate as many snippets as I want, combine them, pass them between PROCS or even return them from procs and at any point, wrap them up in containers all without modifying the data or being forced to add XML structure into my data. I use this approach with HTTP end points to provide XML Web services and with another trick that converts XML into JSON, to provide JSON WebServices.

    -- SETUP A type (or use this design for a Table Variable) to temporarily store snippets into. The pattern can be repeated to pass/store snippets to build
    -- larger elements and those can be further combined following the pattern.
    CREATE TYPE [dbo].[XMLRes] AS TABLE(
        [xmlResult] [xml] NULL
    )
    GO


    -- Call the following as much as you like to build up all the elements you want included in the larger element
    INSERT INTO @XMLRes ( xmlResult )
        SELECT
            (    
                SELECT
                    'foo' '@bar'
                FOR XML
                    PATH('SomeTopLevelElement')
            )

    -- This is the key to "concatenating" many snippets into a larger element. At the end of this, add " ,ROOT('DocumentRoot') " to wrapp them up in another element even
    -- The outer select is a time from user2503764 that controls the output column name

   SELECT (
    SELECT XmlData as [*]
    FROM
        (
        SELECT
            xmlResult AS [*]
        FROM
            @XmlRes
        WHERE
            xmlResult IS NOT NULL
        FOR XML PATH(''), TYPE
        ) as DATA(XmlData)
    FOR XML PATH('')
   ) as [someColumnName]


回答6:

ALTER PROCEDURE usp_fillHDDT @Code  int

AS
BEGIN

 DECLARE @HD XML,@DT XML;  

    SET NOCOUNT ON;
    select invhdcode, invInvoiceNO,invDate,invCusCode,InvAmount into #HD
    from dbo.trnInvoiceHD where invhdcode=@Code

    select invdtSlNo No,invdtitemcode ItemCode,invdtitemcode ItemName,
    invDtRate Rate,invDtQty Qty,invDtAmount Amount ,'Kg' Unit into #DT from
     dbo.trnInvoiceDt  where invDtTrncode=@Code 

    set @HD = (select * from #HD HD  FOR XML AUTO,ELEMENTS XSINIL);
    set @DT = (select* from #DT DT FOR XML AUTO,ELEMENTS XSINIL);

    SELECT CAST ('<OUTPUT>'+ CAST (ISNULL(@HD,'') AS VARCHAR(MAX))+ CAST ( ISNULL(@DT,'') AS VARCHAR(MAX))+ '</OUTPUT>'   AS XML)

END


回答7:

public String ReplaceSpecialChar(String inStr)
{
    inStr = inStr.Replace("&", "&amp;");
    inStr = inStr.Replace("<", "&lt;");
    inStr = inStr.Replace(">", "&gt;");
    inStr = inStr.Replace("'", "&#39;");
    inStr = inStr.Replace("\"", "&quot;");
    return inStr;
}