Разделить несколько столбцов на несколько строк

У меня есть таблица с этой структурой.

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

Я пытаюсь разбить три столбца на три отдельные строки. Это возможно?


person Sandro    schedule 09.09.2010    source источник


Ответы (3)


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, один для номера вопроса и один для названия вопроса. Или, если у вас уже есть таблица со списком вопросов, то ПЕРЕКРЕСТНОЕ СОЕДИНЕНИЕ с ней. Если некоторые вопросы имеют значение NULL, проще всего поместить приведенный выше запрос в CTE или производную таблицу, а затем добавить WHERE Response IS NOT NULL.

person ErikE    schedule 10.09.2010
comment
Я получаю «имя вопроса столбца не существует» с этим - person hedgedandlevered; 09.12.2016
comment
Нет, мой запрос работает нормально. Вы получаете эту ошибку, только пытаясь сослаться на псевдоним QuestionName в другом месте запроса. Поместите этот запрос в производную таблицу и выберите из нее, затем вы можете поставить на него условия. Вы используете SQL Server, верно? - person ErikE; 09.12.2016
comment
О, нет, я использую обычный SQL. Не видел тег sql-server. Спасибо, в итоге понял это, используя ответ 8kb и ваш. - person hedgedandlevered; 09.12.2016
comment
Для других СУБД вам нужно 'AnswerToQuestion' + X.Which AS QuestionName. - person ErikE; 09.12.2016

Предполагая, что 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;
person Martin Smith    schedule 09.09.2010
comment
Извините за поздний ответ и спасибо за ваш ответ. Существуют ли альтернативы использованию оператора UNPIVOT? - person Sandro; 09.09.2010

Согласно Ицику Бен-Гану в внутри Microsoft SQL Server 2008: T- SQL Querying, 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

На этом шаге отфильтровываются все строки, созданные с нулевыми значениями в столбце Response. Другими словами, если какой-либо из столбцов 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. Заполните столбец Response на основе совпадения исходного столбца и 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
person 8kb    schedule 10.09.2010
comment
Большое спасибо за качественный ответ. Мне очень нравится ваш ответ, так как он самый подробный, но, возможно, мне придется дать ответ Emtucifor за то, что он ответил всего за пару часов до вас. Спасибо! - person Sandro; 10.09.2010