2016-11-15 172 views
0

我有以下两个临时表,#dates和#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显示顾客将留在旅馆房间的日期范围,并且这些日期必须(应该)是连续的

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

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

的限制有:

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

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

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

    二〇一六年十二月二十零日 - > $ 200

    2016年12月21日 - > $ 100

    2016年12月22日 - > $ 300

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

我开始与此,

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

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

+0

“@ startdate”变量有什么用? –

+0

如果你愿意,你可以把它作为“Where”的标准。这就像补充数据一样。 – user3174976

回答

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
  • 在同一查询还CROSS APPLY从#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 
+0

这真的很接近我想要的。谢谢!我正在考虑如何过滤结果,其开始日期的statusId等于1,其他非startdate的statusId可以是1或4. – user3174976

+0

hmmm好吧,我们可以使用您的变量并测试它是否可用是连接条件的开始日期..给我一秒钟,我会更新我只是想测试并添加一个case/2到测试数据来适当地测试它 – Matt

+0

@ user3174976我更新了查询,只是改变了ON条件.StatusId = 1 TO - NOT(d.Date = startdateVar AND a.StatusId <> 1)使变量不需要生成另一个公用表表达式(或派生表)来确定最早的日期 – Matt