在 SQL Server 中以 XML 数据类型存储存储过程的文本

Storing the text of a stored procedure in an XML data type in SQL Server

提问人:Gloria Santin 提问时间:9/14/2016 最后编辑:marc_sGloria Santin 更新时间:9/14/2016 访问量:1912

问:

我需要将数据库中所有存储过程的文本存储为 XML 数据类型。当我使用 时,存储过程中的文本包含序列化数据字符,如 CRLF 和 等。我需要将文本存储在没有这些字符的 xml 结构中,因为需要使用文本来重新创建存储过程。FOR XML PATH
"

这是我用于的查询:FOR XML PATH

SELECT 
    [View].name AS "@VName", [Module].definition AS "@VDefinition"
FROM 
    sys.views AS [View] 
INNER JOIN 
    sys.sql_modules AS [Module] ON [Module].object_id = [View].object_id
FOR XML PATH ('View'), TYPE

我读到我应该使用 .但是,当我运行以下查询并查看 XML 数据时,它的输出也包含这些字符。我需要文本是没有这些字符的纯文本。CDATAFOR XML EXPLICIT

这是我的查询:

SELECT  
    1 AS Tag,
    0 AS Parent,
    NULL AS [Database1!1],      
    NULL AS [StoredProcedure!2!VName],
    NULL AS [StoredProcedure!2!cdata]

UNION ALL

SELECT  
    2 AS Tag,
    1 AS Parent,        
    NULL,
    [StoredProcedure].name as [StoredProcedure!2!!CDATA],
    [Module].definition as [StoredProcedure!2!!CDATA]
FROM 
    sys.procedures AS [StoredProcedure] 
INNER JOIN 
    sys.sql_modules [Module] ON [StoredProcedure].object_id = [Module].object_id
WHERE 
    [StoredProcedure].name NOT LIKE '%diagram%'
FOR XML EXPLICIT    

如何存储纯文本存储过程的文本?或者,当我分析xml数据类型以重新创建存储过程时,是否可以对其进行反序列化,使其不包含这些字符?

理想情况下,我想使用,但如果不可能,我将使用 .FOR XML PATHFOR XML EXPLICIT

sql-server xml cdata for-xml-explicit

评论

1赞 btberry 9/14/2016
最终目标是什么?为什么要以固有的可怕格式存储文本?

答:

0赞 M.Hassan 9/14/2016 #1
    SELECT 
        1 as Tag,  
        0 as Parent,    
        [View].name AS 'StoredProcedure!1!Name', 
        [Module].definition AS 'StoredProcedure!1!Definition!cdata'     
    FROM sys.views AS [View] 
    INNER JOIN sys.sql_modules AS [Module] ON [Module].object_id = [View].object_id
    FOR XML EXPLICIT 

Adventureworks2012 的输出示例:

    <StoredProcedure Name="vStoreWithContacts">
      <Definition><![CDATA[
    CREATE VIEW [Sales].[vStoreWithContacts] AS 
    SELECT 
        s.[BusinessEntityID] 
        ,s.[Name] 
        ,ct.[Name] AS [ContactType] 
        ,p.[Title] 
        ,p.[FirstName] 
        ,p.[MiddleName] 
        ,p.[LastName] 
        ,p.[Suffix] 
        ,pp.[PhoneNumber] 
        ,pnt.[Name] AS [PhoneNumberType]
        ,ea.[EmailAddress] 
        ,p.[EmailPromotion] 
    FROM [Sales].[Store] s
        INNER JOIN [Person].[BusinessEntityContact] bec 
        ON bec.[BusinessEntityID] = s.[BusinessEntityID]
        INNER JOIN [Person].[ContactType] ct
        ON ct.[ContactTypeID] = bec.[ContactTypeID]
        INNER JOIN [Person].[Person] p
        ON p.[BusinessEntityID] = bec.[PersonID]
        LEFT OUTER JOIN [Person].[EmailAddress] ea
        ON ea.[BusinessEntityID] = p.[BusinessEntityID]
        LEFT OUTER JOIN [Person].[PersonPhone] pp
        ON pp.[BusinessEntityID] = p.[BusinessEntityID]
        LEFT OUTER JOIN [Person].[PhoneNumberType] pnt
        ON pnt.[PhoneNumberTypeID] = pp.[PhoneNumberTypeID];
    ]]></Definition>
    </StoredProcedure>
    <StoredProcedure Name="vStoreWithAddresses">
      <Definition><![CDATA[
    CREATE VIEW [Sales].[vStoreWithAddresses] AS 
    SELECT 
        s.[BusinessEntityID] 
        ,s.[Name] 
        ,at.[Name] AS [AddressType]
        ,a.[AddressLine1] 
        ,a.[AddressLine2] 
        ,a.[City] 
        ,sp.[Name] AS [StateProvinceName] 
        ,a.[PostalCode] 
        ,cr.[Name] AS [CountryRegionName] 
    FROM [Sales].[Store] s
        INNER JOIN [Person].[BusinessEntityAddress] bea 
        ON bea.[BusinessEntityID] = s.[BusinessEntityID] 
        INNER JOIN [Person].[Address] a 
        ON a.[AddressID] = bea.[AddressID]
        INNER JOIN [Person].[StateProvince] sp 
        ON sp.[StateProvinceID] = a.[StateProvinceID]
        INNER JOIN [Person].[CountryRegion] cr 
        ON cr.[CountryRegionCode] = sp.[CountryRegionCode]
        INNER JOIN [Person].[AddressType] at 
        ON at.[AddressTypeID] = bea.[AddressTypeID];
    ]]></Definition>

如您所见,没有换行符,换行符表示为换行符&#xD; / &#xA; / &quot;/ etc

评论

0赞 Shnugo 9/14/2016
这种方法很好,但我仍然不推荐它...... 没有错,但是 - 至少对我和Microsoft的一些人来说 - 逃跑似乎更好。CDATA
2赞 Shnugo 9/14/2016 #2

如果要在XML中存储带有特殊字符的数据,则有两个选项(外加一个笑话选项)

  • 逃避
  • CDATA
  • 只是提到:将所有内容转换为或类似的东西也可以:-)base64

关键是:你不需要这个!

唯一的原因(至少对我而言)是手动创建的内容(复制粘贴或输入)。每当自动生成 XML 时,都应该依赖于隐式应用的转义。CDATA

为什么它困扰您,数据在 XML 中的外观如何?

如果你正确地阅读了它(而不是使用或其他基于字符串的方法),你会得到它的原始外观SUBSTRING

试试这个:

DECLARE @TextWithSpecialCharacters NVARCHAR(100)=N'€ This is' + CHAR(13) + 'strange <ups, angular brackets! > And Ampersand &&&';

SELECT @TextWithSpecialCharacters FOR XML PATH('test');

返回

€ This is
strange &lt;ups, angular brackets! &gt; And Ampersand &amp;&amp;&amp;

但是这个......

SELECT (SELECT @TextWithSpecialCharacters FOR XML PATH('test'),TYPE).value('/test[1]','nvarchar(100)');

...返回

€ This is
strange <ups, angular brackets! > And Ampersand &&&

Microsoft甚至决定不支持这一点(除了,这是脖子上的痛......FOR XMLEXPLICIT

阅读两个相关答案(由我:-)关于 CDATA)

评论

0赞 AakashM 9/14/2016
嗯,我在打字时没有得到“答案已发布”......我是第一个:)给我+1
0赞 Shnugo 9/14/2016
@AakashM我会偿还这笔钱,因为你的回答和我的一样令人信服。精神兄弟:-)
0赞 Gloria Santin 9/14/2016
谢谢。正如我告诉@AakashM的那样,这将节省我的时间。但我仍然必须提取它。
0赞 Shnugo 9/14/2016
@GloriaSantin,当你想在XML中存储一些东西时,你将有一种或另一种方式来提取它......关键是,所有 XML 工具都隐式地执行了繁重的部分,因此您不必为内部发生的事情而烦恼。顺便说一句:谢谢你的接受!既然你自己已经越过了 15 分的边界,那么投票给 AakashM 的答案是公平的。如果你愿意,你也可以投票给我:-)
0赞 Gloria Santin 9/14/2016
做!是的。你是对的。我可以从 XML 数据类型变量中提取视图和存储过程,并将其作为文本存储到临时表中,它看起来很棒。但是,当我将它们添加到 xml 文档时,有 3 个存储过程在我对查询进行检查时会引发错误。例如,其中一个存储过程引发错误 。incorrect syntax new the keyword Column
2赞 AakashM 9/14/2016 #3

当我使用 FOR XML PATH 时,存储过程中的文本包含序列化数据字符,例如 和 对于 CRLF 和 “等。

是的,因为这就是 XML 的工作方式。举一个更清楚的例子,假设你的 sproc 包含以下文本:

IF @someString = '<' THEN

然后要将其存储在XML中,必须应用某种编码,因为XML中间不能有裸露的编码(我希望您能看到原因)。<

那么,真正的问题不是“当我将文本存储为 XML 时,如何阻止文本被编码”,而是(正如您猜测的那样):

或者,当我分析xml数据类型以重新创建存储过程时,是否可以对其进行反序列化,使其不包含这些字符?

是的,这是您应该考虑的方法。

你不知道我们目前如何从XML中获取你的文本。要记住的关键是,你不能(或者更确切地说不应该)将 XML 视为“带有额外位的文本”——你应该使用理解 XML 的方法。

如果要提取 T-SQL 本身中的文本,请使用各种 XQuery 选项。如果在 C# 中,请使用各种 XML 库中的任何一个。只是不要做子字符串操作并期望它起作用......


例如,如果要在 T-SQL 中提取数据:An example, if you are extracting in T-SQL:

DECLARE @someRandomText nvarchar(max) = 'I am some arbitrary text, eg a sproc definition.

I contain newlines

And arbitrary characters such as < > & 

The end.';

-- Pack into XML

DECLARE @asXml xml = ( SELECT @someRandomText FOR XML PATH ('Example'), TYPE );

SELECT @asXml;


-- Extract

DECLARE @textOut nvarchar(max) = ( SELECT @asXml.value('.', 'nvarchar(max)') ) ;

SELECT @textOut;

但是,您可以找到许多关于如何从 xml 类型数据中获取值的教程;这只是一个例子。

评论

0赞 Gloria Santin 9/14/2016
谢谢你。现在看来我应该知道这一点。你为我节省了很多时间。我会尝试你的建议。