Форматирование сводного результата путем слияния строк в Sql
Фон:
Подумайте о приложении, которое позволяет людям делать опросы с настраиваемыми вопросами. В частном случае интервью семьи, An интервьюер отправляется на House 1
и соберет двух участников Member 1
и Member 2
. Он задает такие вопросы. Что это за дом адрес? Какое ваше имя и возраст? Ответы для что является общим для стран-членов, и ответы, характерные для они сохраняются в одной таблице
После некоторого присоединения к некоторым таблицам и поворота результата я получаю следующую структуру таблицы.
Что было достигнуто до сих пор
| ID | ADDRESS | MEMBER | AGE | SubformIteration |
|----|---------|----------|--------|-------------------|
| 1 | HOUSE 1 | (null) | (null) | (null) |
| 1 | (null) | MEMBER h | 18 | s0 |
| 1 | (null) | MEMBER i | 19 | s1 |
| 2 | HOUSE 2 | (null) | (null) | (null) |
| 2 | (null) | MEMBER x | 36 | s0 |
| 2 | (null) | MEMBER y | 35 | s1 |
| 3 | HOUSE 3 | (null) | (null) | (null) |
| 3 | (null) | MEMBER a | 18 | s0 |
| 3 | (null) | MEMBER b | 19 | s1 |
Я пытаюсь найти способ отформатировать таблицу следующим образом:
Желаемый вывод
| ID | ADDRESS | MEMBER | AGE | SubformIteration |
|----|---------|----------|--------|-------------------|
| 1 | HOUSE 1 | MEMBER 1 | 18 | s0 |
| 1 | HOUSE 1 | MEMBER 2 | 19 | s1 |
| 2 | HOUSE 2 | MEMBER x | 36 | s0 |
| 2 | HOUSE 2 | MEMBER y | 35 | s1 |
| 3 | HOUSE 3 | MEMBER a | 18 | s0 |
| 3 | HOUSE 3 | MEMBER b | 19 | s1 |
У меня недостаточно словарного кода sql для описания и поиска требуемой операции/процедуры. Поскольку я новичок в SQL, и я был бы очень благодарен, если бы кто-нибудь мог сказать мне эффективный способ достичь этого.
Внимание!
НЕ ОТНОСЯТСЯ К колонке QuestionText
, поскольку это будет изменение Когда кто-то решил изменить вопросы
Edit
Исходные таблицы
ссылка Sql со всеми приведенными ниже таблицами
В соответствии с предложениями в ответах, я отправляю исходную таблицу и запросы в надежде, что будет лучшее понимание проблемы.
Questions
таблица
+------------+--------------+---------+----------+---------------+
| QuestionID | QuestionText | type | SurveyID | IsIncremental |
+------------+--------------+---------+----------+---------------+
| 3483 | subform | subform | 311 | 1 |
| 3484 | MEMBER | text | 311 | 0 |
| 3485 | AGE | number | 311 | 0 |
| 3486 | ADDRESS | address | 311 | 0 |
+------------+--------------+---------+----------+---------------+
Results
таблица
+----------+-------------------------+----------+
| ResultID | DateSubmitted | SurveyID |
+----------+-------------------------+----------+
| 2272 | 2017-04-12 05:11:41.477 | 311 |
| 2273 | 2017-04-12 05:12:22.227 | 311 |
| 2274 | 2017-04-12 05:13:02.227 | 311 |
+----------+-------------------------+----------+
Chunks
, где хранятся все ответы:
+---------+------------+----------+------------+------------------+
| ChunkID | Answer | ResultID | QuestionID | SubFormIteration |
+---------+------------+----------+------------+------------------+
| 9606 | HOUSE 1 | 2272 | 3486 | NULL |
| 9607 | MEMEBER 1 | 2272 | 3484 | NULL |
| 9608 | 12 | 2272 | 3485 | NULL |
| 9609 | MEMBER 2 | 2272 | 3484 | s1 |
| 9610 | 10 | 2272 | 3485 | s1 |
| 9611 | MEMEBER 1 | 2272 | 3484 | s0 |
| 9612 | 12 | 2272 | 3485 | s0 |
| 9613 | MEMBER 2 | 2272 | 3484 | s1 |
| 9614 | 10 | 2272 | 3485 | s1 |
| 9615 | HOUSE 2 | 2273 | 3486 | NULL |
| 9616 | MEMBER A | 2273 | 3484 | NULL |
| 9617 | 23 | 2273 | 3485 | NULL |
| 9618 | MEMBER B | 2273 | 3484 | s1 |
| 9619 | 25 | 2273 | 3485 | s1 |
| 9620 | MEMBER A | 2273 | 3484 | s0 |
| 9621 | 23 | 2273 | 3485 | s0 |
| 9622 | MEMBER B | 2273 | 3484 | s1 |
| 9623 | 25 | 2273 | 3485 | s1 |
| 9624 | HOUSE 3 | 2274 | 3486 | NULL |
| 9625 | MEMBER K | 2274 | 3484 | NULL |
| 9626 | 41 | 2274 | 3485 | NULL |
| 9627 | MEMBER J | 2274 | 3484 | s1 |
| 9628 | 26 | 2274 | 3485 | s1 |
| 9629 | MEMBER K | 2274 | 3484 | s0 |
| 9630 | 41 | 2274 | 3485 | s0 |
| 9631 | MEMBER J | 2274 | 3484 | s1 |
| 9632 | 26 | 2274 | 3485 | s1 |
+---------+------------+----------+------------+------------------+
Я написал следующую хранимую процедуру, которая дает первую таблицу, приведенную в этом вопросе:
ALTER PROCEDURE [dbo].[ResultForSurvey] @SurveyID int
AS
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX),@colsAggregated as nvarchar(max);
IF OBJECT_ID('tempdb.dbo.#Temp', 'U') IS NOT NULL
DROP TABLE #Temp;
SELECT *
INTO #Temp
FROM (Select Answer=( case
When Questions.type='checkboxes' or Questions.IsIncremental=1 THEN STUFF((SELECT distinct ',' + c.Answer
FROM Chunks c Where c.ResultID=Results.ResultID and c.QuestionID=Questions.QuestionID and (Chunks.SubFormIteration IS NULL )
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
else Chunks.Answer end),Chunks.SubFormIteration,Questions.QuestionText,Questions.type,Questions.QuestionID,Chunks.ResultID,Results.ResultID as Action,Results.DateSubmitted,Results.Username,Results.SurveyID from Chunks Join Questions on Questions.QuestionID= Chunks.QuestionID Join Results on Results.ResultID=Chunks.ResultID Where [email protected]) as X
SET @colsAggregated = STUFF((SELECT distinct ','+ 'max('+ QUOTENAME(c.QuestionText)+') as '+ QUOTENAME(c.QuestionText)
FROM #Temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
print @colsAggregated
SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.QuestionText)
FROM #Temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT ResultID,max(Username) as Username,max(DateSubmitted) as DateSubmitted,max(SubFormIteration) as SubFormIteration, ' + @colsAggregated + ' from
(
select *
from #Temp
) as y
pivot
(
max(Answer)
for QuestionText in (' + @cols + ')
) as p GROUP BY
ResultID,SubFormIteration'
execute(@query)
Ответы
Ответ 1
Может быть полезно опубликовать запрос, который принес вам ваши исходные результаты; существует вероятность, что исходный запрос можно переписать, чтобы избежать этой сложности. С данной информацией это наиболее упрощенный способ решения этой проблемы:
SELECT
h1.Id,
h2.Address,
h1.Member,
h1.Age,
h1.MemberNo
FROM House h1
INNER JOIN House h2
ON h1.Id = h2.Id
WHERE h2.Address IS NOT NULL -- Eliminates the results whre the Address is NULL after the join
AND h1.Member IS NOT NULL -- Eliminates the results that would show up from the original table (t1) where there is no Member field
Update:
Вот простой пример структуры таблицы, используя временные таблицы:
DROP TABLE #Questions
DROP TABLE #Results
DROP TABLE #Chunks
CREATE TABLE #Questions
(
QuestionId INT,
QuestionText VARCHAR(MAX),
type VARCHAR(MAX),
SurveyID INT,
IsIncremental INT
)
CREATE TABLE #Results
(
ResultId INT,
DateSubmitted DATETIME,
SurveyID INT
)
CREATE TABLE #Chunks
(
ChunkId INT,
Answer VARCHAR(MAX),
ResultId INT,
QuestionId INT,
SubFormIteration VARCHAR(20)
)
INSERT INTO #Results
VALUES (2272, '04-12-2017', 311),
(2273, '04-12-2017', 311),
(2274, '04-12-2017', 311)
INSERT INTO #Chunks
VALUES (9606, 'WhiteHouse', 2272, 3486, NULL),
(9607, 'MEMBER 1', 2272, 3484, NULL),
(9608, '12', 2272, 3485, NULL),
(9609, 'MEMBER 2', 2272, 3484, 's1'),
(9610, '10', 2272, 3485, 's1'),
(9611, 'MEMBER 1', 2272, 3484, 's0'),
(9612, '12', 2272, 3485, 's0'),
(9613, 'MEMBER 2', 2272, 3484, 's1'),
(9614, '10', 2272, 3485, 's1'),
(9615, 'RpBhavan', 2273, 3486, NULL),
(9618, 'MEMBER B', 2273, 3484, 's1'),
(9619, '25', 2273, 3485, 's1'),
(9620, 'MEMBER A', 2273, 3484, 's0'),
(9621, '23', 2273, 3485, 's0')
INSERT INTO #Questions
VALUES (3483, 'subform', 'subform', 311, 1),
( 3484, 'MEMBER', 'text', 311, 0 ),
(3485, 'AGE', 'number', 311, 0),
(3486, 'ADDRESS', 'address', 311, 0)
Вот способ получения результатов, которые вы ищете без использования PIVOT и XML:
; WITH Responses AS (
SELECT
c.ResultId,
QuestionText,
Answer,
c.SubFormIteration
FROM #Chunks c
INNER JOIN #Results r
ON c.ResultId = r.ResultId
INNER JOIN #Questions q
ON q.QuestionId = c.QuestionId
WHERE c.SubFormIteration IS NOT NULL -- Removes the "Address" responses and duplicate Answers
),
FindAddress AS (
-- Pulls ONLY the address for each ResultId
SELECT
ResultId,
MAX(CASE WHEN QuestionText = 'ADDRESS' THEN Answer END) AS [Address]
FROM #Chunks c
INNER JOIN #Questions q
ON q.QuestionId = c.QuestionId
GROUP BY ResultId
)
-- Combines all responses and the address back together
SELECT
r.ResultId,
fa.Address,
MAX(CASE WHEN QuestionText = 'MEMBER' THEN Answer END) AS [MEMBER],
MAX(CASE WHEN QuestionText = 'AGE' THEN Answer END) AS [Age],
SubFormIteration
FROM Responses r
INNER JOIN FindAddress fa
ON fa.ResultId = r.ResultId
GROUP BY r.ResultId, SubFormIteration, fa.Address
По существу, я разбил довольно большой запрос в Common Table Expression (CTE). Каждый запрос имел цель: a) Response вытягивает все ответы, кроме адреса, b) Вытягивает только адрес на основе ResultId и c) Объединяет оба запроса вместе.
MAX (CASE...), за которым следует GROUP BY, является альтернативным методом использования PIVOTS, и они по существу выполняют то же самое.
Чтобы применить этот запрос к вашему конкретному случаю, вам нужно только изменить имя таблиц.
Ответ 2
Насколько я понимаю: вы хотите сделать это динамически. Для этого вам нужно подготовить текст вопроса и запустить его.
Подготавливаются столбцы. Затем сливается с запросом.
DECLARE @Columns NVARCHAR(MAX)
DECLARE @Query NVARCHAR(MAX)
SELECT @Columns = 'C.ResultId' +
(
SELECT
',' +
CASE WHEN COL.QuestionText = 'ADDRESS' THEN 'MAX(AA.Answer)' + COL.QuestionText
ELSE 'MAX(CASE WHEN Q.QuestionText = ''' + COL.QuestionText + ''' THEN C.Answer ELSE '''' END) AS ' + COL.QuestionText END
FROM
#Questions COL
WHERE
COL.QuestionText != 'subform'
FOR XML PATH ('')
) +
',MAX(C.SubFormIteration) AS SubFormIteration'
SET @Query = '
SELECT ' +
@Columns +
' FROM
#Chunks C INNER JOIN
#Results R ON C.ResultId = R.ResultId INNER JOIN
#Questions Q ON Q.QuestionId = C.QuestionId INNER JOIN
(
SELECT
IC.ResultId,
MAX(IC.Answer) AS Answer
FROM
#Chunks IC INNER JOIN
#Results IR ON IC.ResultId = IR.ResultId INNER JOIN
#Questions IQ ON IQ.QuestionId = IC.QuestionId
WHERE
IQ.QuestionText = ''ADDRESS''
GROUP BY
IC.ResultId
) AA ON C.ResultId = AA.ResultId
WHERE
C.SubFormIteration IS NOT NULL
GROUP BY
C.ResultId,
C.SubFormIteration
'
--SELECT @Query
EXEC sp_executesql @Query
Вывод:
ResultId MEMBER AGE ADDRESS SubFormIteration
----------- ----------- ---- ------------ --------------------
2272 MEMBER 1 12 WhiteHouse s0
2272 MEMBER 2 10 WhiteHouse s1
2273 MEMBER A 23 RpBhavan s0
2273 MEMBER B 25 RpBhavan s1
Для комментариев:
Столбцы "ResultId" и "SubFormIteration" сгруппированы и результат. Но операция группировки неверна, потому что информация о адресе выглядит так. Запрос и результат ниже.
ResultId MEMBER AGE ADDRESS SubFormIteration
----------- -------------------------------------------------------
2272 MEMBER 1 12 WhiteHouse NULL -- Which value you want to group. s1 or s0
2272 MEMBER 1 12 s0
2272 MEMBER 2 10 s1
2273 RpBhavan NULL -- Which value you want to group. s1 or s0
2273 MEMBER A 23 s0
2273 MEMBER B 25
Query:
DECLARE @Columns NVARCHAR(MAX)
DECLARE @Query NVARCHAR(MAX)
SELECT @Columns = 'C.ResultId' +
(
SELECT
',' +
'MAX(CASE WHEN Q.QuestionText = ''' + COL.QuestionText + ''' THEN C.Answer ELSE '''' END) AS ' + COL.QuestionText
FROM
#Questions COL
WHERE
COL.QuestionText != 'subform'
FOR XML PATH ('')
) +
',MAX(C.SubFormIteration
) AS SubFormIteration'
SET @Query = '
SELECT ' +
@Columns +
' FROM
#Chunks C INNER JOIN
#Results R ON C.ResultId = R.ResultId INNER JOIN
#Questions Q ON Q.QuestionId = C.QuestionId
GROUP BY
C.ResultId,
C.SubFormIteration
'
--SELECT @Query
EXEC sp_executesql @Query
Ответ 3
Мы не можем видеть ваш входной запрос, но я предполагаю, что вы получаете эти нулевые столбцы из-за объединения LEFT
или RIGHT
в исходном запросе. Если вы можете разделить результат по вертикали на два вида:
| ID | ADDRESS |
|----|---------|
| 1 | HOUSE 1 |
и
| ID | MEMBER | AGE | MEMBERNO |
|----|----------|--------|----------|
| 1 | MEMBER 1 | 18 | 1 |
| 1 | MEMBER 2 | 19 | 2 |
а затем присоедините их к полю ID
, вы получите именно тот результат, который вам нужен.
Изменить
После просмотра вашего редактирования, вот как вы применяете вышеуказанный метод в своем сценарии:
Первый запрос:
SELECT ID, ADDRESS FROM YourTable WHERE ADDRESS IS NOT NULL
Второй запрос:
SELECT MEMBER, AGE, MEMBERNO WHERE MEMBER IS NOT NULL AND AGE IS NOT NULL AND MEMBERNO IS NOT NULL
Теперь соедините их вместе на ID
:
SELECT * FROM
(SELECT ID, ADDRESS FROM YourTable WHERE ADDRESS IS NOT NULL) AS A
INNER JOIN
(SELECT MEMBER, AGE, MEMBERNO WHERE MEMBER IS NOT NULL AND AGE IS NOT NULL AND MEMBERNO IS NOT NULL) AS B
ON A.ID = B.ID
Ответ 4
Если приведенная выше таблица является результатом нескольких объединений /etc, было бы лучше, если бы мы могли дать рекомендацию, основанную на фактической схеме, доступной вам. Однако, если образец таблицы в ссылке SQL Fiddle - это все, с чем вам нужно работать, попробуйте следующее:
SELECT h.address, p.member, p.age, p.memberno
FROM House h
INNER JOIN
House p
ON h.id = p.id
AND h.member IS NULL
AND p.member IS NOT NULL
Ответ 5
Взглянув на ваш ответ, я думаю, вам нужен результат CROSS JOIN
из двух таблиц
Вы можете использовать этот запрос:
SELECT * from table1,table2
Это поможет вам...