2013-08-16 24 views
0

我有一種情況,我試圖從臨時表中將未填充字段的列表轉換爲逗號分隔語句。將動態構建的'for xml'語句插入到表或變量中

所以給出的示例中的數據(這將始終是單個行,並可能在一個臨時表(作爲實際數據將來自源表的多個)):

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的,但不能得到它的工作。

回答

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

+0

感謝,真的需要更多地瞭解使用CTE的:) – cjb110

+0

其實你可以用子查詢做到這一點,但對我來說CTE乾淨多了 –

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