按列名连接表,将字符串转换为列名

Join tables by column names, convert string to column name

我有一个 table,每 1 项调查存储 1 行。
每项调查大约有 70 个问题,每列显示 1 个问题

SurveyID  Q1, Q2   Q3   .....
1         Yes Good Bad  ......

我想旋转它,所以它显示为

SurveyID Question  Answer
1        Q1        Yes
1        Q2        Good
1        Q3        Bad
...      ...       .....

我使用{cross apply}来实现这个

   SELECT t.[SurveyID]
  , x.question
  , x.Answer
  FROM tbl t
  CROSS APPLY 
  (
    select 1 as QuestionNumber, 'Q1' as Question , t.Q1 As Answer union all
    select 2 as QuestionNumber, 'Q2' as Question , t.Q2 As Answer union all
    select 3 as QuestionNumber, 'Q3' as Question , t.Q3 As Answer) x

这行得通,但我不想这样做 70 次,所以我有这个 select 声明

    select ORDINAL_POSITION
    , COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = mytable

这给了我 table 中列的列表和列的位置。 所以我希望我可以通过列名以某种方式将第二条语句与第一条语句连接起来。但是,我在这里比较一列和一列 header 中的内容。可行吗?还有其他方法可以实现这一目标吗?

希望你能指导我吗?

谢谢

对于此查询,您应该使用 UNPIVOT 而不是交叉应用....

SQL Fiddle

MS SQL Server 2008 架构设置:

CREATE TABLE Test_Table(SurveyID  INT, Q1 VARCHAR(10)
                                     , Q2 VARCHAR(10),  Q3 VARCHAR(10), Q4 VARCHAR(10))

INSERT INTO Test_Table VALUES
 (1 , 'Yes',  'Good' ,  'Bad',  'Bad')  
,(2 ,  'Bad',  'Bad' , 'Yes' ,  'Good')

查询 1:

SELECT SurveyID
      ,Questions
      ,Answers
FROM Test_Table t 
 UNPIVOT ( Answers FOR Questions IN (Q1,Q2,Q3,Q4))up

Results:

| SurveyID | Questions | Answers |
|----------|-----------|---------|
|        1 |        Q1 |     Yes |
|        1 |        Q2 |    Good |
|        1 |        Q3 |     Bad |
|        1 |        Q4 |     Bad |
|        2 |        Q1 |     Bad |
|        2 |        Q2 |     Bad |
|        2 |        Q3 |     Yes |
|        2 |        Q4 |    Good |

如果您需要对许多具有不同列数的类似表执行此类操作,单独使用 UNPIVOT 方法可能会很麻烦,因为您必须手动更改列列表 (Q1,Q2,Q3,etc) 每次。

问题中基于 CROSS APPLY 的查询也有类似的缺点。

如您所料,解决方案涉及使用服务器维护的元信息来告诉您需要操作的列的列表。但是,不像您怀疑的那样需要某种 join,而是需要 Dynamic SQL,即 SQL 即时创建另一个 SQL 查询的查询。

这主要是通过在查询的 SELECT 部分连接字符串 (varchar) 信息来完成的,包括来自 FROM(和连接)子句中可用的列的值。

使用动态 SQL (DSQL) 方法,您经常使用系统元表作为起点。 INFORMATION_SCHEMA 存在于某些 SQL 服务器版本中,但您最好为此使用 Object Catalog Views

为您的 CROSS APPLY 方法生成代码的原型 DSQL 解决方案如下所示:

-- Create a variable to hold the created SQL code
-- First, add the static code at the start:
declare @SQL varchar(max) = 
'  SELECT t.[SurveyID]
  , x.question
  , x.Answer
  FROM tbl t
  CROSS APPLY 
  (
'

-- This syntax will add to the variable for every row in the query results; it's a little like looping over all the rows. 
select @SQL +=
     'select ' + cast(C.column_id as varchar)
    + ' as QuestionNumber, ''' + C.name 
    + ''' as Question , t.' + C.name
    + ' As Answer union all
    '
from sys.columns C
inner join sys.tables T on C.object_id=T.object_id
where T.name = 'MySurveyTable'

-- Remove final "union all", add closing bracket and alias
set @SQL = left(@SQL,len(@SQL)-10) + ') x'

print @SQL

-- To also execute (run) the dynamically-generated SQL
-- and get your desired row-based output all at the same time, 
-- use the EXECUTE keyword (EXEC for short)

exec @SQL

可以使用类似的方法为 UNPIVOT 方法动态编写 SQL。