Sql Server FOR XML AUTO并返回用户定义的表类型?

时间:2013-08-19 07:36:24

标签: sql sql-server xml sql-server-2008 sqlxml

我想将存储过程的结果转换回一个特定的用户定义数据类型。

这是基于此处提出的想法:http://sqlblogcasts.com/blogs/danny/archive/2008/01/06/for-xml-and-back-again.aspx

例如,如果我有一个声明为:

的类型
CREATE TYPE [dbo].[MyType]
AS TABLE 
(
    Id INT IDENTITY,
    MonthNumber INT,     
    YearNumber INT,      
    NumberOfOfficersMakingReferrals INT, 
    TierLevel NVARCHAR(10),                       
    TierStrengthTotal INT                
)
GO

这样称呼:

DECLARE @Data [dbo].[MyType]

INSERT INTO @Data
EXEC [dbo].[sp_MyProc]

SET @Xml = (SELECT * FROM @Data FOR XML AUTO)
SELECT  @Xml

-- Get a table back
EXEC uSpShredUserDefinedTableType @Xml, '[dbo].[MyType]'
GO

但是我编写的SP返回NULL,因为XML的NODE名称不会返回@UserTableType:

CREATE PROC dbo.uSpShredUserDefinedTableType @Xml XML, @UserTableType SYSNAME AS

       DECLARE @Sql NVARCHAR(MAX)

       SELECT @Sql = 'SELECT ' +
           STUFF((SELECT '      ,T.Data.value(''@' +
                         c.name + ''', ''' +
                         t.name +
                         CASE WHEN c.user_type_id IN (165,167,173,175,231,239)
                              THEN '(' + CONVERT(VARCHAR, c.max_length) + ')'
                              WHEN c.user_type_id IN (106, 108)
                              THEN '(' + CONVERT(VARCHAR, c.precision)
                                   + ', ' + CONVERT(VARCHAR, c.scale) + ')'
                              ELSE '' END +
                         ''') AS ' + c.name + CHAR(10)
                  FROM   sys.columns c
                         INNER JOIN sys.types t ON c.user_type_id = t.user_type_id
                  WHERE  object_id IN (SELECT type_table_object_id
                                       FROM sys.table_types
                                       WHERE name = @UserTableType)
                         AND t.name !='xml'
                  FOR XML PATH('')), 1, 7, '') +
           'FROM   @Xml.nodes(''/' + @UserTableType + ''') T(Data)'


    EXEC sp_executesql @Sql, N'@Xml XML', @Xml = @Xml
GO

所以任何人都可以在上面的存储过程中看到我出错的地方,这样我可以使其功能与Danny的示例相同,但是对于用户定义的表类型?

[编辑:最终解决方案]

我不得不将Devart的解决方案修改为:

  • 正确处理“[”,因为SQL生成错误'''附近的语法错误,预计是“节点测试”。'
  • 修复声明为(MAX)的任何内容的生成,因为生成NVARCHAR(-1)

PROC:

CREATE PROC [generator].[sp_ShredUserDefinedTableType] 
    @AXml XML, 
    @AUserTableType SYSNAME
AS
   DECLARE @Sql NVARCHAR(MAX)
   SELECT @Sql = 'SELECT ' +
        STUFF((SELECT '      ,T.Data.value(''@' +
                        c.name + ''', ''' +
                        t.name +
                        CASE WHEN c.user_type_id IN (165,167,173,175,231,239)
                             THEN '(' + CASE (c.max_length)
                                             WHEN -1 THEN 'MAX'
                                        ELSE
                                           CONVERT(VARCHAR, c.max_length) 
                                        END + ')'
                            WHEN c.user_type_id IN (106, 108)
                            THEN '(' + CONVERT(VARCHAR, c.precision) + ', ' + CONVERT(VARCHAR, c.scale) + ')'
                        ELSE 
                           '' 
                        END + ''') AS ' + c.name + CHAR(10)
        FROM sys.table_types tt 
        JOIN sys.columns c ON tt.type_table_object_id = c.[object_id]
        JOIN sys.types t ON c.user_type_id = t.user_type_id
        WHERE SCHEMA_NAME(tt.[schema_id]) + '.' + tt.name = @AUserTableType
            AND t.name != 'xml'
        FOR XML PATH('')), 1, 7, '') +
        'FROM   @Xml.nodes(''/' + @AUserTableType + ''') T(Data)'

    -- Print out the SQL that is necessary to query the XML that has been generated
    PRINT @sql
GO

可以按如下方式调用:

DECLARE @Xml XML
DECLARE @Data [dbo].[t_sp_Report_AR102_DataRetrieval_V1_1_Result]

INSERT INTO @Data
EXEC [dbo].[sp_Report_AR102_DataRetrieval_V1_1]
     @AOfficerParticipationNumberOfPriorDays = 30
    ,@AStartDate = '2013-04-01' 
    ,@AEndDate = '2013-06-30' 
    ,@AReferringServiceID = 4
    ,@AExcludePresentingIssueIDs = ''
    ,@AResultAsXML = @Xml OUTPUT


SET @Xml = (SELECT * FROM @Data FOR XML raw('dbo.t_sp_Report_AR102_DataRetrieval_V1_1_Result'))
EXEC [generator].[sp_ShredUserDefinedTableType] @Xml, 'dbo.t_sp_Report_AR102_DataRetrieval_V1_1_Result'

2 个答案:

答案 0 :(得分:3)

这里有几个问题:

  • 您的表是表变量,因此要获取架构,您必须查询sys.table_types
  • 当您选择for xml for auto时,您的节点元素名称将是xml safe @Data - <_x0040_Data ...,因此我建议用户xml path

您的代码变为:

CREATE PROC dbo.uSpShredUserDefinedTableType @Xml XML, @UserTableType SYSNAME AS

       DECLARE @Sql NVARCHAR(MAX)

   SELECT @Sql = 'SELECT ' +
        STUFF((SELECT '      ,T.Data.value(''@' +
                        c.name + ''', ''' +
                        t.name +
                        CASE WHEN c.user_type_id IN (165,167,173,175,231,239)
                            THEN '(' + CONVERT(VARCHAR, c.max_length) + ')'
                            WHEN c.user_type_id IN (106, 108)
                            THEN '(' + CONVERT(VARCHAR, c.precision)
                                + ', ' + CONVERT(VARCHAR, c.scale) + ')'
                            ELSE '' END +
                        ''') AS ' + c.name + CHAR(10)
        FROM sys.table_types tt 
            JOIN sys.columns c ON tt.type_table_object_id = c.[object_id]
            JOIN sys.types t ON c.user_type_id = t.user_type_id
        WHERE tt.name = @UserTableType
            AND t.name != 'xml'
        FOR XML PATH('')), 1, 7, '') +
        'FROM   @Xml.nodes(''/' + @UserTableType + ''') T(Data)'

    --select @sql
    EXEC sp_executesql @Sql, N'@Xml XML', @Xml = @Xml
GO

你称之为

SET @Xml = (SELECT * FROM @Data FOR XML raw('MyType'))

EXEC uSpShredUserDefinedTableType @Xml, 'MyType'

sql fiddle demo

答案 1 :(得分:2)

试试这个 -

   DECLARE @Xml XML, @UserTableType SYSNAME = '[dbo].[MyType]'
   DECLARE @Sql NVARCHAR(MAX)
   SELECT @Sql = 'SELECT ' +
        STUFF((SELECT '      ,T.Data.value(''@' +
                        c.name + ''', ''' +
                        t.name +
                        CASE WHEN c.user_type_id IN (165,167,173,175,231,239)
                            THEN '(' + CONVERT(VARCHAR, c.max_length) + ')'
                            WHEN c.user_type_id IN (106, 108)
                            THEN '(' + CONVERT(VARCHAR, c.precision)
                                + ', ' + CONVERT(VARCHAR, c.scale) + ')'
                            ELSE '' END +
                        ''') AS ' + c.name + CHAR(10)
        FROM sys.table_types tt 
        JOIN sys.columns c ON tt.type_table_object_id = c.[object_id]
        JOIN sys.types t ON c.user_type_id = t.user_type_id
        -- your mistake: [dbo].[MyType] != MyType          
        WHERE '[' + SCHEMA_NAME(tt.[schema_id]) + '].[' + tt.name + ']' = @UserTableType
            AND t.name != 'xml'
        FOR XML PATH('')), 1, 7, '') +
        'FROM   @Xml.nodes(''/' + @UserTableType + ''') T(Data)'

PRINT @Sql

输出 -

SELECT T.Data.value('@Id', 'int') AS Id
      ,T.Data.value('@MonthNumber', 'int') AS MonthNumber
      ,T.Data.value('@YearNumber', 'int') AS YearNumber
      ,T.Data.value('@NumberOfOfficersMakingReferrals', 'int') AS NumberOfOfficersMakingReferrals
      ,T.Data.value('@TierLevel', 'nvarchar(20)') AS TierLevel
      ,T.Data.value('@TierStrengthTotal', 'int') AS TierStrengthTotal
FROM   @Xml.nodes('/[dbo].[MyType]') T(Data)