背景:
考虑一个允许人们使用自定义问题进行调查的应用程序,在特定情况下,采访家庭,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
列,因为当有人决定更改问题时它会发生变化
修改
来源表
Sql fiddle link with all the below tables
根据答案中的建议,我发布源表和查询,希望能更好地理解问题
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 Results.SurveyID=@SurveyID) 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)
答案 0 :(得分:4)
发布获得原始结果的查询可能会有所帮助;有可能重写原始查询以避免这种复杂性。根据给定的信息,这是解决此问题的最简单方法:
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
以下是使用临时表的表结构的简单示例:
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
基本上,我将一个相当大的查询打破了公用表表达式(CTE)。每个查询都有一个目的:a)响应拉除地址之外的所有响应,b)仅基于ResultId拉取地址,c)将两个查询组合在一起。
MAX(CASE ...)后跟GROUP BY是使用PIVOTS的另一种方法,它们基本上执行相同的操作。
要将此查询应用于您的特定情况,您只需要更改表的名称。
答案 1 :(得分:3)
据我了解:你想动态地这样做。为此,您需要准备问题文本并运行它。
正在准备这些专栏。然后与查询合并。
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
评论:
Columns" 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
查询:
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
答案 2 :(得分:2)
我们无法看到您的输入查询,但我猜您因为源查询中的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
答案 3 :(得分:2)
如果上表是多个连接/ 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
答案 4 :(得分:1)
通过寻找你的答案,我认为你需要两个表{{1}}的结果
您可以使用此查询:
CROSS JOIN
这会对你有帮助......