将动态构建的“for xml”语句插入表或变量中

时间:2013-08-16 12:34:22

标签: sql-server xml tsql sqlxml

我遇到的情况是,我试图将临时表中未填充的字段列表放到逗号分隔的语句中。

所以给出了示例数据(它总是一行,可能在临时表中(因为实际数据来自多个源表)):

Field1     Field2   Field3   Field4
'aaa'      null      ''      null

的映射表
FieldName    Question   Section
'Field1'     'Q1'       'Sec1'
'Field2'     'Q2'       'Sec1'
'Field3'     'Q3'       'Sec2'
'Field4'     'Q4'       'Sec2'

我想要以下结果:

Section   UnansweredQs
'Sec1'    'Q2'
'Sec2'    'Q3, Q4'

我可以通过以下方式获得以逗号分隔的问题列表:

create table #testData (f1 varchar(50), f2 int, f3 varchar(50), f4 varchar(50))
create table #qlist (fieldName varchar(5), question varchar(3), section varchar(5))

insert into #qlist values ('f1', 'q1', 'sec1'), ('f2', 'q2', 'sec1'), ('f3', 'q3', 'sec2'), ('f4', 'q4', 'sec2')

insert into #testData values ('asda', null, '', null)

然后

declare @usql nvarchar(max) = ''
declare @sql nvarchar(max)
declare @xml xml

--build a gargantuan set of union statements, comparing the column value to null/'' and putting q# if it is
set @usql = 
    (
    select 'select case when ' + c.name + ' is null or ' + c.Name + ' = '''' then ''' + q.question + ', '' else '''' end from #testData union '
    from tempdb..syscolumns c
    inner join #qlist q
        on c.name = q.fieldName
    where c.id = object_id('tempdb..#testData') 
    for xml path('')
    );
--remove the last 'union', append for xml path to pivot the rows into a single column of concatenated rows
set @usql = left(@usql, len(@usql) - 6) + ' for xml path('''')'

print @usql

--remove final comma
--get the position of the last comma in the select statment (ie after the final unanswered question)
declare @lastComma int = charindex(',', reverse(@usql))
--add the bit before the last comma, and the bit after the last comma but skip the actual comma :)
set @usql = left(@usql, len(@usql) - @lastComma) + right(@usql, @lastComma - 2)

exec (@usql)

有了这个,我得到了

XML_F52E2B61-18A1-11d1-B105-00805F49916B
----------------------------------------
q2, q3, q4

但我无法将结果集转换为另一个表或变量(通过insert into #tmpresult exec (@usql)方法)。

通常会出现Msg 1086, Level 15, State 1, Line 1 The FOR XML clause is invalid in views, inline functions, derived tables, and subqueries when they contain a set operator. To work around, wrap the SELECT containing a set operator using derived table syntax and apply FOR XML on top of it.错误。

我尝试了各种各样的东西,包装,移除工会,CTE但无法使其工作。

2 个答案:

答案 0 :(得分:1)

我有一个问题:

with cte as (
    select
        N.Name
    from Table1
        cross apply (values
           ('Field1', Field1),
           ('Field2', Field2),
           ('Field3', Field3),
           ('Field4', Field4)
        ) as N(Name,Value)
    where N.Value is null or N.Value = ''
)
select distinct
    T2.Section,
    stuff(
        (
            select ', ' + TT.Question
            from Table2 as TT
                inner join cte as c on c.Name = TT.FieldName
            where TT.Section = T2.Section
            for xml path(''), type
        ).value('.', 'nvarchar(max)')
    , 1, 2, '') as UnansweredQs
from Table2 as T2

你可以自己把它变成动态:)

sql fiddle demo

答案 1 :(得分:1)

无需使用动态SQL来执行此操作。

declare @X xml

set @X = (
         select *
         from #testData
         for xml path('root'), elements xsinil, type 
         )

select section,
       (
       select ', '+Q2.question
       from #qlist as Q2
       where Q1.section = Q2.section and
             @X.exist('/root/*[local-name() = sql:column("Q2.fieldName")][. = ""]') = 1
       for xml path(''), type
       ).value('substring(text()[1], 2)', 'varchar(max)') as UnansweredQs
from #qlist as Q1
group by Q1.section

SQL Fiddle