【问题标题】:Complex SQL Pivot query复杂的 SQL Pivot 查询
【发布时间】:2016-03-17 16:11:47
【问题描述】:

一个快速的背景,让我的问题有意义:系统以问卷的形式从用户那里收集数据。用户属于组织,组织属于部门,问题/计算(如调查问卷中所示)因部门而异。 (问题由用户回答;计算由系统计算)。

存在以下表格:

部门(部门 ID、名称)
组织(组织 ID、名称、部门 ID)
年份(年份 ID、名称)

问题(QuestionID、DisplayText、CommonName、SectorID)
答案(AnswerID、Answer、OrganisationID、YearID、QuestionID)

计算(CalculationID、DisplayText、CommonName、SectorID)
CalculationResults(CalculationResultID、Result、OrganisationID、YearID、CalculationID)

我需要通过以下方式显示数据:

让这个问题(对我来说)特别复杂的是,问题以不同的方式(向用户)显示在它们所属的不同部门中,但其中一些仍然是常见问题。例如。 “制造销售”与“销售(制造)”相同。我需要使用CommonName 字段来确定共性。

我已经设法使用 SQL Pivot 来接近我想要的 - SQL Fiddle(如果您运行 SQL,您会注意到空值和“共性”问题)。但是我的尝试中缺少一些东西:

  1. 通用性和列名 - 我需要列名是 CommonName 字段,而不是 QuestionID 字段。

  2. 我只从 Answers 表中进行了选择 - 我还需要从结构相同的 CalculationResults 表中进行选择。

    李>

编辑:SQL Fiddle 数据的期望结果是: (带有橙色角的两个块需要一直向左移动,以便问题共有 3 列 - 3 个唯一的 CommonName 值。接下来的 3 列是 3 个唯一的 @987654330 @ 计算值。我希望我说得通,如果不告诉我。)

Edit2:另一个编辑只是为了好玩。我确实考虑过重新设计数据库,但在这个阶段这不是一个选择——在这个遗留系统上风险太大。以防有人看到设计并认为。希望我需要 Pivot 形式的解决方案。

【问题讨论】:

  • 您能否为您在 sqlfiddle 中提供的演示提供具有所需结果(列名和值)的图片?
  • @lad2025,编辑了原帖

标签: sql tsql sql-server-2012 pivot


【解决方案1】:

有时您可以使用[Aggregate](CASE EXPRESSION) 代替PIVOT 来获取相同的数据。有时它会更快。

对于您的问题,您可以使用OUTER APPLY 和动态MAX(CASE)

DECLARE @Questions NVARCHAR(MAX),
        @Calculations NVARCHAR(MAX),
        @Sql NVARCHAR(MAX)

SELECT  @Questions = COALESCE(@Questions + ', ', '') 
            + 'MAX(CASE WHEN q.CommonName = ''' + CommonName + ''' THEN a.Answer END) AS ' + QUOTENAME(CommonName)
FROM    Questions 
GROUP BY CommonName

SELECT  @Calculations = COALESCE(@Calculations + ', ', '') 
            + 'MAX(CASE WHEN c.CommonName = ''' + CommonName + ''' THEN cr.Result END) AS ' + QUOTENAME(CommonName)
FROM    Calculations  
GROUP BY CommonName

SET     @Sql = N'
        SELECT 
            o.Name As [Organisation],
            y.Name As [Year],
            q.*,
            c.*
        FROM 
            Organisations o
            CROSS JOIN Years y
            OUTER APPLY (
                SELECT ' + @Questions + ' 
                FROM    Answers a 
                        JOIN Questions q ON a.QuestionID = q.QuestionID
                WHERE   a.OrganisationID = o.OrganisationID 
                        AND a.YearID = y.YearID
            ) q
            OUTER APPLY (
                SELECT ' + @Calculations + ' 
                FROM    CalculationResults cr 
                        JOIN Calculations c ON cr.CalculationID = c.CalculationID
                WHERE   cr.OrganisationID = o.OrganisationID 
                        AND cr.YearID = y.YearID
            ) c
'

SQL FIDDLE DEMO

【讨论】:

  • 绝对是答案!地狱与“避免像'+1'这样的cmets”! +1! (嗯,不,我尊重规则,有点,我会在你看到这条评论后删除它。+1。谢谢你的努力。)
  • 很高兴我能帮上忙
【解决方案2】:

基本上我们想要得到QuestionIDGrouped BySectorIDName的顺序。

您可以使用PARTITION BY 执行此操作,如下所示:

ROW_NUMBER() OVER(PARTITION BY q.SectorID, y.Name ORDER BY a.QuestionID)

应该这样做:

    DECLARE @cols AS  NVARCHAR(MAX)
     , @query AS NVARCHAR(MAX);

SELECT                     @cols = STUFF(
                                 (SELECT DISTINCT
                                        ','+QUOTENAME(CAST(ROW_NUMBER() OVER(PARTITION BY q.SectorID
                                                                              , y.Name ORDER BY a.QuestionID) AS VARCHAR(10)))
                                  FROM   Answers a
                                        LEFT JOIN Years y ON a.YearID = y.YearID
                                        LEFT JOIN Organisations o ON a.OrganisationID = o.OrganisationID
                                        LEFT JOIN Questions q ON a.QuestionID = q.QuestionID
                                  FOR XML PATH(''), TYPE).value
      ('.', 'NVARCHAR(MAX)'), 1, 1, '');

SET @query = '
            SELECT Organisation, Year, '+@cols+' from 
(
 SELECT QuestionID = ROW_NUMBER() OVER(PARTITION BY q.SectorID
                                              , y.Name ORDER BY a.QuestionID)
           , o.Name AS Organisation
           , y.Name AS Year
           , a.Answer
       FROM   Answers a
            LEFT JOIN Years y ON a.YearID = y.YearID
            LEFT JOIN Organisations o ON a.OrganisationID = o.OrganisationID
            LEFT JOIN Questions q ON a.QuestionID = q.QuestionID
) src
pivot
(
  max(Answer)
  for QuestionID in ('+@cols+')
) piv
order by Organisation, Year
';

PRINT(@query);

EXECUTE (@query);

结果:

【讨论】:

  • 感谢您抽出宝贵时间(讨厌这个问题如此复杂,但我无法以其他方式表达)。我尝试在 SQL Fiddle 上运行它时遇到错误,尽管它看起来很有希望并且我将使用它。它也缺少列命名,并且不查询两个表,只查询答案。但真的,感谢您的努力
  • 我不确定是谁否决了你的问题,但我认为它写得很好。特别是我喜欢您包含示例数据和 SQL 小提琴的事实。真的很容易工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-29
  • 1970-01-01
  • 2012-06-03
  • 1970-01-01
相关资源
最近更新 更多