如何在没有额外XML开销的情况下在T SQL中编码XML

时间:2016-03-18 15:42:59

标签: sql sql-server xml tsql

我有一个数据库(无论出于何种原因)有一个包含管道分隔数据的列。

我想快速解析这些数据,所以我想通过用XML属性替换管道并将其放入其他地方的XML数据类型列中,将此列(nvarchar)转换为XML。

它有效,除非该列具有需要编码的字符,例如'<'字符。

我发现我可以使用FOR XML子句对XML进行编码,但是,它似乎会在数据周围注入一些XML标记。

例如:(这会给不良角色带来错误)

SELECT CAST('<f>' + replace(value,'|','</f><f>') + '</f>' AS XML)
FROM TABLE

这给出了xml编码值,但将其包装在“&lt; value&gt;&lt; / value&gt;”中标签

  SELECT value
   FROM table
  FOR XML PATH('')

关于如何在没有添加额外标记的情况下获取XML编码值的任何想法,所以我可以在完成后将管道格式转换为XML(最好是一次性)?

编辑:因为人们在问,这是5个潜在的数据行可能是什么样的

foo
foo|bar
foo|bar|1
foo||
baz|

结果将是

Col1, Col2, Col3
foo,null,null
foo,bar,null
foo,bar,1
foo,null,null
baz,null,null

我通过在子查询中使用生成的XML类型来实现这一点,例如:(在任何给定的行中最多可以有4列pr 3个管道)

    SELECT 
          *,
         x.query('f[1]').value('.','nVarChar(2048)')  Col1
        ,x.query('f[2]').value('.','nVarChar(2048)')  Col2
        ,x.query('f[3]').value('.','nvarchar(2048)')  Col3
        ,x.query('f[4]').value('.','nvarchar(2048)')  Col4
    FROM
    (
        SELECT *,
        CAST('<f>' + REPLACE(Value,'|','</f><f>') + '</f>' AS XML) as x
     FROM  table
    ) y

@srutzky提出了一个很好的观点。不,我根本不需要在这里做XML。如果我能找到一个快速的&amp;在基于集合的操作中解析管道的干净方法,我会这样做。将查看SQL#文档...

3 个答案:

答案 0 :(得分:1)

SELECT CAST('<values><f>' + 
    REPLACE(
        REPLACE(
            REPLACE(
                REPLACE(
                    REPLACE(value,'&','&amp;')
                    ,'"','&quot;')
                ,'<','&lt;')
            ,'>','&gt;')
        ,'|','</f><f>') + '</f></values>' AS XML)
FROM TABLE;

答案 1 :(得分:1)

您可以尝试以下方法但是您需要确保内容是“xml safe”,换句话说,内容不包含xml将拒绝的值(查看xml元素内容解析)。 尝试以下...它是测试脚本,看它是否符合您的要求..

UPDATE:
好吧,如果我一直读完这个问题可能会有所帮助... 2步......拆分管道然后xml所有拆分项......试试这个:

创建以下功能:

CREATE FUNCTION [dbo].[udf_SPLIT]
(
  @s nvarchar(max),
  @trimPieces bit,
  @returnEmptyStrings bit,
  @delimiter nvarchar(10)
)
RETURNS @t TABLE (val nvarchar(max))
AS
BEGIN
    DECLARE @i int, @j int

    SELECT @i = 0, @j = (LEN(@s) - LEN(REPLACE(@s,@delimiter,'')))

    ;WITH cte AS
    (
        SELECT i = @i + 1,
            s = @s,
            n = substring(@s, 0, charindex(@delimiter, @s)),
            m = substring(@s, charindex(@delimiter, @s)+1, len(@s) - charindex(@delimiter, @s))
            UNION ALL
        SELECT i = cte.i + 1,
            s = cte.m, 
            n = substring(cte.m, 0, charindex(@delimiter, cte.m)),
            m = substring(cte.m, charindex(@delimiter, cte.m) + 1, len(cte.m)-charindex(@delimiter, cte.m))
        FROM cte
        WHERE i <= @j
    )
    INSERT INTO @t (val)
        SELECT [pieces]
        FROM (
            SELECT CASE 
                WHEN @trimPieces = 1 THEN LTRIM(RTRIM(CASE WHEN i <= @j THEN n ELSE m END))
                ELSE CASE WHEN i <= @j THEN n ELSE m END
                END AS [pieces]
            FROM cte
            ) t
        WHERE (@returnEmptyStrings = 0 AND LEN(pieces) > 0)
        OR (@returnEmptyStrings = 1)
        OPTION (maxrecursion 0)

RETURN

END

接下来尝试以下测试...

DECLARE @str nvarchar(500) = 'test|<html>this</html>|boogie woogie| SDGDSFG| game<br /> on |working| this|'

SELECT REPLACE(
        REPLACE(
            REPLACE(
                REPLACE([val],'&','&amp;')
                ,'"','&quot;')
            ,'<','&lt;')
        ,'>','&gt;')
     AS [f]
FROM [dbo].[udf_SPLIT](@str,1,0,'|')
FOR XML PATH('')

如果不完全正确,希望能让你走上正确的道路......

HTH

戴夫

答案 2 :(得分:1)

您的想法绝对正常:通过从字符串中生成XML,XML引擎将正确转换所有特殊字符。拆分后,XML应该是正确的。

如果您的字符串存储在列中,您可以通过执行某种计算(类似'' + YourColumn)来避免自动给定的名称,或者为列添加别名{{1 }}:

试试这样:

AS [*]