将多列拆分为多行

发布于 2024-09-18 01:30:46 字数 663 浏览 5 评论 0原文

我有一个具有这种结构的表。

UserID  | UserName  | AnswerToQuestion1 | AnswerToQuestion2 | AnswerToQuestion3
1       | John      | 1                 | 0                 | 1
2       | Mary      | 1                 | 1                 | 0

我无法弄清楚我将使用什么 SQL 查询来获得这样的结果集:

UserID  | UserName  | QuestionName      | Response
1       | John      | AnswerToQuestion1 | 1
1       | John      | AnswerToQuestion2 | 0
1       | John      | AnswerToQuestion3 | 1
2       | Mary      | AnswerToQuestion1 | 1
2       | Mary      | AnswerToQuestion2 | 1
2       | Mary      | AnswerToQuestion3 | 0

我试图将三列拆分为三个单独的行。这可能吗?

I have a table with this structure.

UserID  | UserName  | AnswerToQuestion1 | AnswerToQuestion2 | AnswerToQuestion3
1       | John      | 1                 | 0                 | 1
2       | Mary      | 1                 | 1                 | 0

I can't figure out what SQL query I would use to get a result set like this:

UserID  | UserName  | QuestionName      | Response
1       | John      | AnswerToQuestion1 | 1
1       | John      | AnswerToQuestion2 | 0
1       | John      | AnswerToQuestion3 | 1
2       | Mary      | AnswerToQuestion1 | 1
2       | Mary      | AnswerToQuestion2 | 1
2       | Mary      | AnswerToQuestion3 | 0

I'm trying to split the three columns into three separate rows. Is this possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

凉墨 2024-09-25 01:31:13

假设 SQL Server 2005+ 您可以使用 UNPIVOT

;with YourTable as
(
SELECT 1 UserID,'John' UserName,1 AnswerToQuestion1,0 AnswerToQuestion2,1 AnswerToQuestion3 
UNION ALL
SELECT 2, 'Mary', 1, 1, 0
)
SELECT UserID, UserName, QuestionName, Response
FROM YourTable
UNPIVOT
   (Response FOR QuestionName IN 
      (AnswerToQuestion1, AnswerToQuestion2,AnswerToQuestion3)
)AS unpvt;

Assuming SQL Server 2005+ you can use UNPIVOT

;with YourTable as
(
SELECT 1 UserID,'John' UserName,1 AnswerToQuestion1,0 AnswerToQuestion2,1 AnswerToQuestion3 
UNION ALL
SELECT 2, 'Mary', 1, 1, 0
)
SELECT UserID, UserName, QuestionName, Response
FROM YourTable
UNPIVOT
   (Response FOR QuestionName IN 
      (AnswerToQuestion1, AnswerToQuestion2,AnswerToQuestion3)
)AS unpvt;
想念有你 2024-09-25 01:31:09

根据 Itzik Ben-Gan 在Inside Microsoft SQL Server 2008:T-SQL 查询中的说法,SQL Server 在逆透视表时会经历三个步骤:

  1. 生成副本
  2. 提取元素
  3. 删除包含 NULL 的行

第 1 步:生成副本

将创建一个虚拟表,该表对于要取消透视的每一列都有原始表中每一行的副本。
此外,列名称的字符串存储在新列(称为 QuestionName 列)中。 *注意:我将其中一列中的值修改为 NULL 以显示完整的过程。

UserID  UserName  AnswerTo1 AnswerToQ2 AnswerToQ3 QuestionName
1       John      1         0          1          AnswerToQuestion1
1       John      1         0          1          AnswerToQuestion2
1       John      1         0          1          AnswerToQuestion3
2       Mary      1         NULL       1          AnswerToQuestion1
2       Mary      1         NULL       1          AnswerToQuestion2
2       Mary      1         NULL       1          AnswerToQuestion3

第 2 步:提取元素

然后创建另一个表,为对应的源列中的每个值创建一个新行
到 QuestionName 列中的字符串值。该值存储在一个新列中(称为响应列)。

UserID  UserName  QuestionName        Response
1       John      AnswerToQuestion1   1
1       John      AnswerToQuestion2   0
1       John      AnswerToQuestion3   1
2       Mary      AnswerToQuestion1   1
2       Mary      AnswerToQuestion2   NULL
2       Mary      AnswerToQuestion3   1

步骤 3:删除包含 NULL 的行

此步骤会过滤掉在“响应”列中使用空值创建的任何行。换句话说,
如果任何 AnswerToQuestion 列具有空值,则它不会表示为未透视的行。

UserID  UserName  QuestionName        Response
1       John      AnswerToQuestion1   1
1       John      AnswerToQuestion2   0
1       John      AnswerToQuestion3   1
2       Mary      AnswerToQuestion1   1
2       Mary      AnswerToQuestion3   1

如果您按照这些步骤操作,则可以

  1. 针对每个 AnswerToQuestion 交叉连接表中的所有行
    用于获取行副本的列名称
  2. 填充响应列
    在匹配源列和 QuestionName 时
  3. 删除 NULL 以获得相同的结果
    不使用 UNPIVOT 的结果。

下面是一个例子:

DECLARE @t1 TABLE (UserID INT, UserName VARCHAR(10), AnswerToQuestion1 INT, 
  AnswertoQuestion2 INT, AnswerToQuestion3 INT
) 

INSERT @t1 SELECT 1, 'John', 1, 0, 1 UNION ALL SELECT 2, 'Mary', 1, NULL, 1 

SELECT
  UserID,
  UserName,
  QuestionName,
  Response
FROM (
  SELECT
    UserID,
    UserName,
    QuestionName,
    CASE QuestionName
      WHEN 'AnswerToQuestion1' THEN AnswerToQuestion1
      WHEN 'AnswerToQuestion2' THEN AnswertoQuestion2
      ELSE AnswerToQuestion3 
    END AS Response 
  FROM @t1 t1
      CROSS JOIN (
        SELECT 'AnswerToQuestion1' AS QuestionName
        UNION ALL SELECT 'AnswerToQuestion2'
        UNION ALL SELECT 'AnswerToQuestion3'
      ) t2
    ) t3
WHERE Response IS NOT NULL

According to Itzik Ben-Gan in Inside Microsoft SQL Server 2008: T-SQL Querying, SQL Server goes through three steps when unpivoting a table:

  1. Generate copies
  2. Extract elements
  3. Remove rows with NULLs

Step 1: Generate copies

A virtual table is created that has a copy of each row in the orignal table for each column that is being unpivoted.
Also, a character string of the column name is stored in a new column (call this the QuestionName column). *Note: I modified the value in one of your columns to NULL to show the full process.

UserID  UserName  AnswerTo1 AnswerToQ2 AnswerToQ3 QuestionName
1       John      1         0          1          AnswerToQuestion1
1       John      1         0          1          AnswerToQuestion2
1       John      1         0          1          AnswerToQuestion3
2       Mary      1         NULL       1          AnswerToQuestion1
2       Mary      1         NULL       1          AnswerToQuestion2
2       Mary      1         NULL       1          AnswerToQuestion3

Step 2: Extract elements

Then another table is created that creates a new row for each value from the source column which corresponds
to the character string value in the QuestionName column. The value is stored in a new column (call this the Response column).

UserID  UserName  QuestionName        Response
1       John      AnswerToQuestion1   1
1       John      AnswerToQuestion2   0
1       John      AnswerToQuestion3   1
2       Mary      AnswerToQuestion1   1
2       Mary      AnswerToQuestion2   NULL
2       Mary      AnswerToQuestion3   1

Step 3: Remove rows with NULLS

This step filters out any rows that were created with null values in the Response column. In other words,
if any of the AnswerToQuestion columns had a null value, it would not be represented as an unpivoted row.

UserID  UserName  QuestionName        Response
1       John      AnswerToQuestion1   1
1       John      AnswerToQuestion2   0
1       John      AnswerToQuestion3   1
2       Mary      AnswerToQuestion1   1
2       Mary      AnswerToQuestion3   1

If you follow those steps, you can

  1. CROSS JOIN all rows in the table against each AnswerToQuestion
    column name to get row copies
  2. Populate the Response column based
    on the matching the source column and QuestionName
  3. Remove the NULLs to get the same
    results without using UNPIVOT.

An example below:

DECLARE @t1 TABLE (UserID INT, UserName VARCHAR(10), AnswerToQuestion1 INT, 
  AnswertoQuestion2 INT, AnswerToQuestion3 INT
) 

INSERT @t1 SELECT 1, 'John', 1, 0, 1 UNION ALL SELECT 2, 'Mary', 1, NULL, 1 

SELECT
  UserID,
  UserName,
  QuestionName,
  Response
FROM (
  SELECT
    UserID,
    UserName,
    QuestionName,
    CASE QuestionName
      WHEN 'AnswerToQuestion1' THEN AnswerToQuestion1
      WHEN 'AnswerToQuestion2' THEN AnswertoQuestion2
      ELSE AnswerToQuestion3 
    END AS Response 
  FROM @t1 t1
      CROSS JOIN (
        SELECT 'AnswerToQuestion1' AS QuestionName
        UNION ALL SELECT 'AnswerToQuestion2'
        UNION ALL SELECT 'AnswerToQuestion3'
      ) t2
    ) t3
WHERE Response IS NOT NULL
回忆凄美了谁 2024-09-25 01:31:05
SELECT
   Y.UserID,
   Y.UserName,
   QuestionName = 'AnswerToQuestion' + X.Which,
   Response =
      CASE X.Which
      WHEN '1' THEN AnswerToQuestion1
      WHEN '2' THEN AnswerToQuestion2
      WHEN '3' THEN AnswerToQuestion3
      END
FROM
   YourTable Y
   CROSS JOIN (SELECT '1' UNION ALL SELECT '2' UNION ALL SELECT '3') X (Which)

这与 UNPIVOT 的性能同样好(有时更好),并且也适用于 SQL 2000。

我利用问题的相似性来创建 QuestionName 列,但这当然适用于不同的问题名称。

请注意,如果您的问题列表很长或问题名称很长,您可以尝试在 X 表中使用 2 列,一列用于问题编号,一列用于问题名称。或者,如果您已经有一个包含问题列表的表格,则可以交叉连接到该表格。如果某些问题为 NULL,那么最简单的方法是将上述查询放入 CTE 或派生表中,然后添加 WHERE Response IS NOT NULL

SELECT
   Y.UserID,
   Y.UserName,
   QuestionName = 'AnswerToQuestion' + X.Which,
   Response =
      CASE X.Which
      WHEN '1' THEN AnswerToQuestion1
      WHEN '2' THEN AnswerToQuestion2
      WHEN '3' THEN AnswerToQuestion3
      END
FROM
   YourTable Y
   CROSS JOIN (SELECT '1' UNION ALL SELECT '2' UNION ALL SELECT '3') X (Which)

This performs equally well to UNPIVOT (sometimes better) and works in SQL 2000 as well.

I took advantage of the questions' similarity to create the QuestionName column, but of course this will work with varying question names.

Note that if your list of questions is long or the question names are long, you might experiment with 2 columns in the X table, one for the question number and one for the question name. Or if you already have a table with the list of questions, then CROSS JOIN to that. If some questions are NULL then easiest is to put the above query in a CTE or derived table and then add WHERE Response IS NOT NULL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文