SQL SELECT中聚合值的总和

时间:2014-02-12 20:56:03

标签: sql sql-server

我有一个带有id和name字段的简单属性表。我还有一个propertyPrices表,其架构和数据如下所示;

enter image description here

因此,它指定了给定日期范围的价格。价格值表示该期间内一天的费用。

如果给定开始日期和结束日期,我希望能够根据每个日期范围之间的天数选择价格总和。

我尝试了以下内容;

DECLARE @propertyID int = 1;
DECLARE @startDate date = '2014/02/13';
DECLARE @endDate date = '2014/02/18';

/* get number of days between start and end date */
DECLARE @duration int = DATEDIFF(DAY, @startDate, @endDate)

/* select properties with holiday cost */
SELECT 
    property.name, SUM(dbo.propertyPrices.price) * @duration AS totalCost

FROM 
    dbo.property INNER JOIN dbo.propertyPrices ON dbo.property.id = dbo.propertyPrices.propertyID

WHERE 
    property.id = @propertyID

GROUP BY 
    dbo.property.name

返回2000作为总费用。这是5天,然后乘以400(这是所有价格的总和)。我只想为该范围内的每一天选择合适的价格。所以它应该是;

(100天2天)+(300天3天)= 1100.

我不确定如何汇总个人日值。我的首要任务是表现。

提前感谢您的帮助。

尼克

3 个答案:

答案 0 :(得分:2)

我会用CTE来做。首先获得具有实际日期范围的价格,然后将价格乘以该范围内的天数并将其相加。

with cte as
(
  select propertyID, price,
  case when startDate < @startDate then @startDate else startDate end as startDate, 
  case when endDate > @endDate then @endDate else endDate end as endDate
  from propertyPrices
  where endDate >= @startDate
  and startDate <= @endDate
)
, cte2 as
(
  select propertyID, sum((datediff(day, startDate, endDate)+1) * price) as totalCost
  from cte
  group by propertyID
)
select name, totalCost
from cte2
inner join property on id = propertyID
where id = @propertyID

您可以在SQLFiddle

上查看整个解决方案

答案 1 :(得分:1)

我可以想象编写一个存储过程,或者另一个解决方案是构建一个只包含日期列表的辅助表。

如果您的表calendar(date)的所有日期都在受影响的范围内,则可以执行

SELECT 
    property.name, SUM(dbo.propertyPrices.price) AS totalCost

FROM 
    dbo.property
    INNER JOIN dbo.propertyPrices
        ON dbo.property.id = dbo.propertyPrices.propertyID
    JOIN calendar
        ON calendar.date between startDate and endDate

WHERE 
    property.id = @propertyID

GROUP BY 
    dbo.property.name

答案 2 :(得分:0)

我相信您需要做的就是在原始查询中添加一个case语句,定义您希望应用于开始日期和结束日期的日期边界和组的条件,这会创建一个表,您可以将其用作子表查询父母按属性名称选择哪些组。

请参阅下面的代码,我已经使用了表varibales,所以我可以轻松地测试它,但你应该可以编辑自己的表名,只需删除@。

DECLARE @property AS Table(
    id INT 
,   name NVARCHAR(50)
)

INSERT INTO @property
SELECT 1, 'Property 1' UNION ALL
SELECT 2, 'Property 2'


DECLARE @propertyPrices AS Table(
    id INT 
,   propertyID INT
,   startDate datetime
,   endDate datetime
,   price float
)

INSERT INTO @propertyPrices
SELECT 1, 1, '2014-02-10', '2014-02-15', 100.00 UNION ALL
SELECT 2, 1, '2014-02-16', '2014-02-20', 300.00

DECLARE @propertyID int = 1;
DECLARE @startDate date = '2014/02/13';
DECLARE @endDate date = '2014/02/18';

/* get number of days between start and end date */
DECLARE @duration int = DATEDIFF(DAY, @startDate, @endDate)

/* select properties with holiday cost */
SELECT p.name, SUM(totalCost) FROM (
SELECT 
        p.name
    ,   CASE
            WHEN @startDate > pp.startDate AND @endDate > pp.endDate THEN 
                SUM(pp.price) * DATEDIFF(DAY, @startDate, @endDate)
            WHEN @endDate < pp.endDate THEN 
                SUM(pp.price) * DATEDIFF(DAY, pp.startDate, @endDate)
            ELSE 
                SUM(pp.price) * DATEDIFF(DAY, pp.startDate, pp.endDate)
        END
         AS totalCost

FROM 
    @property p INNER JOIN @propertyPrices pp ON p.id = pp.propertyID

WHERE 
    p.id = @propertyID

GROUP BY 
    p.name, pp.startDate, pp.endDate
) p GROUP BY p.name