如何在按多列分组时查找平均值?

时间:2016-11-15 20:49:53

标签: sql-server group-by

我有两个临时表,#date和#availability,以及一个datetime varaible,@ startdate。

变量是:

declare @startdate datetime = '2016-12-20' 

#dates的列和数据是:

Date
-------
2016-12-20
2016-12-21
2016-12-22

#availability的列和数据是:

GroupId     Date           StatusId     Price
-----------------------------------------------
111       2016-12-20        1           200
111       2016-12-21        1           100
111       2016-12-22        1           500
111       2016-12-22        1           300
222       2016-12-20        4           100 
222       2016-12-21        1           200
222       2016-12-22        1           200
333       2016-12-20        1           100
333       2016-12-22        4           200

表#dates显示客户将留在酒店房间的日期范围,这些日期必须(应该)是连续的

表#availability是我从其他表中获得的可用性数据。

我的目标是显示每个有效群体的平均价格

限制是:

  1. 对于开始日期,StatusId必须为1;否则,StatusId可以是1或4(但不能是其他数字,如2和3) 这意味着GroupId 2已从我们的结果中删除。

  2. 如果该群组没有日期范围的完整价格信息,我们会将其删除。 GroupId 3没有2016-12-21的价格信息,它将被删除。

  3. 如果任何一天有多个价格,我们会选择当天的最低价格。 这意味着第1组将使用以下数据计算平均价格:

    2016-12-20 - > $ 200

    2016-12-21 - > $ 100

    2016-12-22 - > $ 300

    然后,显示最终平均价格:$(200 + 100 + 300)/ 3天= $ 200

  4. 我从这开始,

    Select GroupId,Date
    From #availability
    Group by GroupId, Date
    

    但无法弄清楚如何判断日期编号是否与#dates表匹配,并且每个组的#availability中的startdate状态必须为1。

1 个答案:

答案 0 :(得分:1)

有几种方法可以做到这一点。这是一种不需要日期连续的方法,并且不依赖于日期和GroupIds之间的笛卡尔连接,这应该有助于提高性能。

;WITH cteMinPricePerDay AS (
    SELECT
       d.Date
       ,GroupId
       ,StatusId
       ,MIN(Price) as Price
       ,COUNT(d.Date) OVER (PARTITION BY GroupId) GroupDateCount
       ,dc.DateCount
    FROM
       #date d
       CROSS APPLY (SELECT COUNT(*) as DateCount FROM #date) dc
       LEFT JOIN #availability a
       ON d.Date = a.Date
       AND NOT(d.Date = @startdate AND a.StatusId <> 1)
    GROUP BY
       d.Date
       ,GroupId
       ,StatusId
       ,dc.DateCount
)

SELECT
    GroupId
    ,Date
    ,StatusId
    ,Price
    ,AVG(Price) OVER (PARTITION BY GroupId) as AvgPrice
FROM
    cteMinPricePerDay
WHERE
    GroupDateCount = DateCount

步骤/描述

  • 创建一个分组,以获得每个GroupId每天的最低价格
  • 在同一查询中也交叉应用#dates表中的日期计数,用于确定某个组是否包含所有日期。
  • 生成每组的日期计数
  • 接下来从公用表表达式中选择,其中组具有与#date表相同的天数
  • 添加AVG()窗口函数以计算AveragePrice

这里有一个很好的衡量方法是使用不同组和日期之间的笛卡尔连接。

;WITH cteDistinctGroups AS (
    SELECT DISTINCT GroupId
    FROM
       #availability
)

, cteMinPricePerDay AS (
    SELECT
       d.Date
       ,g.GroupId
       ,MIN(a.Price) as Price
       ,COUNT(CASE WHEN a.Date IS NULL THEN 1 END) OVER (PARTITION BY g.GroupId) as GroupMissingDateCount
    FROM
       #date d
       CROSS JOIN cteDistinctGroups g
       LEFT JOIN #availability a
       ON d.Date = a.Date
       AND g.GroupId = a.GroupId
       AND NOT(d.Date = @startdate AND a.StatusId <> 1)
    GROUP BY
       d.Date
       ,a.Date
       ,g.GroupId
)

SELECT
    GroupId
    ,Date
    ,Price
    ,AVG(Price) OVER (PARTITION BY GroupId) as AveragePrice
FROM
    cteMinPricePerDay
WHERE
    GroupMissingDateCount = 0