Хранение текста хранимой процедуры в типе данных XML в SQL Server

Мне нужно сохранить текст всех хранимых процедур в базе данных в типе данных XML. Когда я использую FOR XML PATH, текст внутри хранимой процедуры содержит сериализованные символы данных, такие как 
 и 
 для CRLF и " и т. д. Мне нужно, чтобы текст сохранялся в структуре xml без этих символов, потому что текст нужно будет использовать для воссоздать хранимую процедуру.

Это запрос, который я использую для 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

Я читал, что я должен использовать CDATA для текста, используя FOR XML EXPLICIT. Однако вывод, когда я запускаю следующий запрос и просматриваю данные XML, также содержит эти символы. Мне нужно, чтобы текст был в виде простого текста без этих символов.

Это мой запрос:

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 PATH, но если это невозможно, я буду использовать FOR XML EXPLICIT.


person Gloria Santin    schedule 13.09.2016    source источник
comment
Какова конечная цель? Почему вы хотите хранить текст в изначально ужасном формате?   -  person btberry    schedule 14.09.2016


Ответы (3)


Если вы хотите хранить данные со специальными символами внутри XML, есть два варианта (плюс шуточный вариант)

  • побег
  • CDATA
  • просто отметим: преобразование всего в base64 или подобное тоже сработает :-)

Дело в том, что вам это не нужно!

Единственная причина для CDATA (по крайней мере, для меня) — это содержимое, созданное вручную (копирование и вставка). Всякий раз, когда вы создаете свой XML автоматически, вы должны полагаться на неявно применяемое экранирование.

Почему вас беспокоит, как данные выглядят в 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 XML (кроме EXPLICIT, который вызывает головную боль...)

Прочитайте два связанных ответа (от меня :-) о CDATA)

person Shnugo    schedule 14.09.2016
comment
Хм, я не получил "ответ опубликован", пока печатал... Примите +1 от меня за то, что я первый :) - person AakashM; 14.09.2016
comment
@AakashM Я верну это, потому что твой ответ так же убедителен, как и мой. Ментальные братья :-) - person Shnugo; 14.09.2016
comment
Спасибо. Как я уже сказал @AakashM, это сэкономит мне время. Но мне еще предстоит извлечь его. - person Gloria Santin; 14.09.2016
comment
@GloriaSantin, поскольку вы хотите сохранить что-то внутри XML, вам придется - так или иначе - извлечь это ... Дело в том, что все инструменты XML делают тяжелая часть неявно, поэтому вам не нужно беспокоиться о том, что происходит внутри. И кстати: Спасибо за принятие! Поскольку вы сами пересекли границу в 15 баллов, было бы справедливо проголосовать за ответ AakashM. А если хотите, можете и за меня проголосовать :-) - person Shnugo; 14.09.2016
comment
Готово! Да. Вы были правы. Я могу извлечь представление и хранимую процедуру из переменной типа данных XML и сохранить их во временной таблице в виде текста, и это выглядит великолепно. Тем не менее, есть 3 хранимые процедуры, когда я добавляю их в документ xml, они выдают ошибку, когда я проверяю запрос. Например, одна из хранимых процедур выдает ошибку incorrect syntax new the keyword Column. - person Gloria Santin; 14.09.2016

Когда я использую FOR XML PATH, текст в хранимой процедуре содержит сериализованные символы данных, такие как и для CRLF и и т. д.

Да, потому что именно так работает XML. Чтобы взять более ясный пример, предположим, что ваш sproc содержит этот текст:

IF @someString = '<' THEN

затем, чтобы сохранить его в XML, должна применяться какая-то кодировка, поскольку у вас не может быть голого < в середине вашего XML (надеюсь, вы понимаете, почему).

Тогда реальный вопрос заключается не в том, «как мне остановить кодирование моего текста, когда я сохраняю его как XML», а скорее (как вы догадываетесь):

Или когда я анализирую тип данных xml для воссоздания хранимой процедуры, могу ли я десериализовать ее, чтобы в ней не было этих символов?

Да, это подход, на который вы должны обратить внимание.

Вы не знаете, как вы получаете текст из XML в данный момент. Главное, что нужно помнить, это то, что вы не можете (или, скорее, не должны) рассматривать XML как «текст с дополнительными битами» — вы должны использовать методы, которые понимают XML.

Если вы извлекаете текст в самом T-SQL, используйте различные параметры XQuery. Если на C#, используйте любую из различных библиотек XML. Просто не выполняйте операцию с подстрокой и ожидайте, что это сработает...


Пример, если вы извлекаете в 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; это просто пример.

person AakashM    schedule 14.09.2016
comment
Спасибо тебе за это. Теперь кажется, что я должен был это знать. Вы сэкономили мне много времени. Я попробую ваши предложения. - person Gloria Santin; 14.09.2016

    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 отсутствуют, а символы новой строки представлены как новая строка.

person M.Hassan    schedule 13.09.2016
comment
Этот подход хорош, и все же я бы не рекомендовал его... CDATA не является неправильным, но, по крайней мере, для меня и некоторых людей в Microsoft, экранирование кажется лучшим. - person Shnugo; 14.09.2016