【问题标题】:Join tables by column names, convert string to column name按列名连接表,将字符串转换为列名
【发布时间】:2015-12-14 21:14:28
【问题描述】:

我有一个表格,每 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 ORDINAL_POSITION
    , COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = mytable

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

希望你能指导我吗?

谢谢

【问题讨论】:

  • 除非您使用动态 sql,否则无法加入结果集。更容易使用 select 语句来制作 70 个 select 语句,然后使用编辑器复制这些语句。这将为您节省一些打字时间。

标签: sql-server tsql pivot cross-apply


【解决方案1】:

你应该使用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 比 CROSS APPLY 简洁得多,并且正是针对这种情况而设计的。但是,这并不能真正回答问题,因为 OP 希望避免写出 SQL 来覆盖 70 列;这仍然需要输入列。 (我接受有这方面的快捷方式,例如从对象资源管理器中的 Columns 文件夹中拖动。)
  • 我也探索过这个功能,但我在这样的结构中有几个表格,问题的数量是可变的。我想知道是否有一种我不需要输入Q1-Q70之类的方法?感谢您的帮助。
  • @PeddiePooh 我的替代答案应该可以为您提供一种方法,您看到了吗?
【解决方案2】:

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

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

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

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

使用动态 SQL (DSQL) 方法时,您经常使用系统元表作为起点。 INFORMATION_SCHEMA 存在于某些 SQL Server 版本中,但您最好为此使用 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。

【讨论】:

  • 请注意,我写这篇文章很匆忙,没有能力在实际的 SQL Server 机器上进行测试;如果有人发现错别字或小错误,请纠正他们,我深表歉意;-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-22
  • 1970-01-01
  • 2019-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多