首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SQL交叉选项卡查询

SQL交叉选项卡查询
EN

Stack Overflow用户
提问于 2014-08-29 14:40:27
回答 3查看 4.6K关注 0票数 2

需要帮助找出如何在一个查询中执行跨表报表。涉及3-4个表,但用户表可能不需要包含在查询中,因为我们只需要一个计数。

我将表、模式和数据的屏幕截图作为示例,如下所示:

我需要它返回的是一个如下所示的查询结果:

所以我可以做一份报告,看起来:

我试着做游标循环,因为这是我用基本知识可以做的唯一方法,但它太慢了。

我正在尝试生成的一个特定报告包含32行和64列,包含大约70000个答案,所以这都取决于将其简化为一个查询的性能以及尽可能快的速度。

我知道这可能取决于索引等等,但是如果有人能帮我弄清楚如何在1查询中完成(多个联接?),那就太棒了!

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-08-29 16:16:04

代码语言:javascript
复制
SELECT MIN(ro.OptionText) RowOptionText, MIN(co.OptionText) RowOptionText, COUNT(ca.AnswerID) AnswerCount
FROM tblQuestions rq 
CROSS JOIN tblQuestions cq 
JOIN tblOptions ro ON rq.QuestionID = ro.QuestionID
JOIN tblOptions co ON cq.QuestionID = co.QuestionID
LEFT JOIN tblAnswers ra ON ra.OptionID = ro.OptionID
LEFT JOIN tblAnswers ca ON ca.OptionID = co.OptionID AND ca.UserID = ra.UserID
WHERE rq.questionText = 'Gender'
AND cq.questionText = 'How happy are you?'
GROUP BY ro.OptionID, co.OptionID
ORDER BY ro.OptionID, co.OptionID

这至少应该接近你的要求。将其转换为支点将需要动态SQL,因为Server要求您指定将被旋转到列中的实际值。

我们交叉连接这些问题,并将每个问题引用的结果分别限制为行值和列值的单个问题。然后将选项值加入到相应的问题引用中。如果用户没有回答所有的问题,我们使用左联接作为答案。我们通过UserID加入答案,以便为每个用户匹配行问题和列问题。选项文本上的最小值是因为我们按照OptionID进行分组和排序,以匹配您所显示的顺序。

编辑:这是一个SQLFiddle

至于它的价值,您的查询是复杂的,因为您使用的是实体属性值设计模式。相当多的Server专家认为这种模式是有问题的,如果可能的话应该避免。例如,请参见https://www.simple-talk.com/sql/t-sql-programming/avoiding-the-eav-of-destruction/

编辑2:既然您接受了我的答案,下面是动态solution解决方案:) SQLFiddle

代码语言:javascript
复制
DECLARE @SqlCmd NVARCHAR(MAX)

SELECT @SqlCmd = N'SELECT RowOptionText, ' + STUFF(
    (SELECT ', ' + QUOTENAME(o.OptionID) + ' AS ' + QUOTENAME(o.OptionText)
    FROM tblOptions o 
    WHERE o.QuestionID = cq.QuestionID
    FOR XML PATH ('')), 1, 2, '') + ', RowTotal AS [Row Total]
FROM (
    SELECT ro.OptionID RowOptionID, ro.OptionText RowOptionText, co.OptionID ColOptionID,
       ca.UserID, COUNT(ca.UserID) OVER (PARTITION BY ra.OptionID) AS RowTotal
    FROM tblOptions ro
    JOIN tblOptions co ON ro.QuestionID = ' + CAST(rq.QuestionID AS VARCHAR(10)) + 
    ' AND co.QuestionID = ' + CAST(cq.QuestionID AS VARCHAR(10)) + '
    LEFT JOIN tblAnswers ra ON ra.OptionID = ro.OptionID
    LEFT JOIN tblAnswers ca ON ca.OptionID = co.OptionID AND ca.UserID = ra.UserID
    UNION ALL 
    SELECT 999999, ''Column Total'' RowOptionText, co.OptionID ColOptionID,
       ca.UserID, COUNT(ca.UserID) OVER () AS RowTotal
    FROM tblOptions ro
    JOIN tblOptions co ON ro.QuestionID = ' + CAST(rq.QuestionID AS VARCHAR(10)) + 
    ' AND co.QuestionID = ' + CAST(cq.QuestionID AS VARCHAR(10)) + '
    LEFT JOIN tblAnswers ra ON ra.OptionID = ro.OptionID
    LEFT JOIN tblAnswers ca ON ca.OptionID = co.OptionID AND ca.UserID = ra.UserID
) t
PIVOT (COUNT(UserID) FOR ColOptionID IN (' + STUFF(
    (SELECT ', ' + QUOTENAME(o.OptionID) 
    FROM tblOptions o 
    WHERE o.QuestionID = cq.QuestionID
    FOR XML PATH ('')), 1, 2, '') + ')) p
ORDER BY RowOptionID'
FROM tblQuestions rq 
CROSS JOIN tblQuestions cq 
WHERE rq.questionText = 'Gender' 
AND cq.questionText = 'How happy are you?'

EXEC sp_executesql @SqlCmd
票数 1
EN

Stack Overflow用户

发布于 2014-08-29 16:15:08

我想我看到问题了。我知道您不能修改模式,但是您需要一个概念表,用于交叉表信息,比如哪个questionID是rowHeader,哪个是colHeader。您可以在外部数据源中创建它,并与现有数据源连接,或者简单地对sql中的表值进行硬编码。

您需要有两个问题/选项/答案关系的实例,每个rowHeader和每个交叉表的colHeader都有一个实例。这两种关系是由userID加入的。

此版本有外部联接:sqlFiddle

这个版本没有crossTab表,只有硬编码的行和col问题‘t:sqlFiddleNoTbl

票数 1
EN

Stack Overflow用户

发布于 2014-08-29 16:48:19

下面的混乱没有硬编码值,但无法显示计数为0的行。然而,对于您的报告来说,这可能仍然有效。

代码语言:javascript
复制
;with stepone as(

SELECT
    RANK() OVER(PARTITION BY a.UserId ORDER BY o.QuestionID) AS [temprank]
,   o.QuestionID AS [QID1]
,   o.OptionID AS [OID1]
,   same.QuestionID
,   same.OptionID
,   a.UserId AS [IDUser]
,   same.UserId
FROM
    tblAnswers a
    INNER JOIN
    tblOptions o
        ON a.OptionID = o.OptionID
    INNER JOIN
    tblQuestions q
        ON o.QuestionID = q.QuestionID
    INNER JOIN
    (
    SELECT
        a.AnswerID
    ,   a.OptionID
    ,   a.UserId
    ,   o.QuestionID    
    FROM
        tblAnswers a
        INNER JOIN
        tblOptions o
            ON a.OptionID = o.OptionID
    ) same
        ON a.UserId = same.UserId AND a.AnswerID <> same.AnswerID

)

, stepthree AS(
SELECT
    t.QID1, t.OID1, t.QuestionID, t.OptionID
,   COUNT(UserId) AS myCount
FROM 
    stepone t
WHERE t.temprank = 1
GROUP BY
    t.QID1, t.OID1, t.QuestionID, t.OptionID
)

SELECT
    o1.OptionText AS [RowTest]
,   o2.OptionText AS [ColumnText]
,   t.myCount AS [Count]

FROM
    stepthree t
    INNER JOIN tblOptions o1
        ON t.OID1 = o1.OptionID
    INNER JOIN tblOptions o2
        ON t.OptionID = o2.OptionID
ORDER BY t.OID1

希望它能帮上忙,我喜欢尝试。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25570614

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档