计算给定标准费率和季节性日期范围的价格

Calculate price given standard rates and seasonal date ranges

我正在构建预订样式系统。这些房间有一个年度标准费率(这是我的问题与其他类似问题的不同之处,因为其他房间有独特的范围)然后他们也有一年内的日期范围,这些日期范围设置为不同的价格。我想计算 MYSQL 中的价格,但是当用户选择在特殊日期范围内的日期时,它会对该价格求和,但也会对这些日期的标准价格求和,这意味着总数太高了。这有点罗嗦,所以这里是查询和示例:

SELECT SUM(Price * (1 + DATEDIFF(LEAST(End_date, '2015-07-25' - INTERVAL 1 DAY), GREATEST(Start_date, '2015-07-15')))) AS Total
FROM room_rates
WHERE roomId = '46' AND (
       '2015-07-25' - INTERVAL 1 DAY BETWEEN Start_date AND End_date 
    OR '2015-07-15'                  BETWEEN Start_date AND End_date
)
+--------+------------+------------+------------+------+
| RoomId | Range Name | Start_Date |  End_Date  | Rate |
+--------+------------+------------+------------+------+
|   46   |  Standard  | 2015-01-01 | 2015-12-31 | 100  |
+--------+------------+------------+------------+------+
|   46   |   Summer   | 2015-07-20 | 2015-08-31 | 150  |
+--------+------------+------------+------------+------+
|   46   |  Christmas | 2015-12-18 | 2015-12-31 | 180  |
+--------+------------+------------+------------+------+

如果用户在我查询的例子中选择2015-07-15到2015-07-25,我想要的是这样计算:

总计应为 1250。但是因为标准汇率的日期介于一年的第一天和最后一天之间,查询还包括该价格和该范围内的总汇率在夏季日期期间,这意味着我得到所有 10 个日期的标准费率,加上夏季费率的 5 晚,总计 1750。

所以我的问题是,如果没有其他选择,我该如何修改查询以仅使用标准费率?标准费率总是被称为 "Standard" 所以我很容易识别它们,我只是不知道要做出什么改变!

编辑

我应该补充一下,我希望在 PHP(我正在使用 PDO)中执行此操作

第二次编辑 另外值得注意的是,日期范围总是在一年之内,并且没有日期范围可以重叠(标准费率除外,它占据了整年)

解决方案

决定改变我的方法并采用几个人建议的答案并建立一个日常数据库。这是我最后的工作查询。感谢大家的帮助和建议。

SELECT standard_rates.villaId as `villaId`,
            sum(IFNULL(custom_rates.nightly_rate_usd, standard_rates.nightly_rate_usd))
        AS `Rate`
        FROM dates

          LEFT JOIN
            villa_price_bands AS standard_rates
            ON standard_rates.Name = 'Standard'
            AND dates.date BETWEEN standard_rates.Start_Date AND standard_rates.End_Date
            AND FIND_IN_SET(standard_rates.villaId, :resultIds)

          LEFT JOIN
            villa_price_bands AS custom_rates
            ON custom_rates.Name != 'Standard'
            AND dates.date BETWEEN custom_rates.Start_Date AND custom_rates.End_Date
            AND custom_rates.villaId = standard_rates.villaId

        WHERE dates.date >= :arrDate
        AND dates.date < :deptDate
        GROUP BY villaId

使用 MySQL CASE 语句。

例如

SELECT Columnname,
  sum(CASE 
          WHEN RangeName LIKE '%Standard%' 
          THEN do something END) as AliasName
FROM YourTable 

我之前处理这类问题的方法是构建一个 'date' table.

1/1/1990 .. 1/1/2050(你可以一次性完成,但为了几 Mb 的存储,'real' [=29= 的性能提升] 让它值得设置一次。

例如

|Date ID| Date        |
|---------------------|
|0000001| 2015-01-01  |
|0000002| 2015-01-02  |

然后你就可以基于这个构建视图了。如:

Select 
    [date id], 
    if(range_name='standard',rate,0) standardPrices,
    if(range_name='Summer',rate,0) summerPrices,
    if(range_name='Winter',rate,0) winterPrices  
from 
    date_table left join room_rates 
on date_table.date between start_date and end_date

where room = '46' and date_table.date between '2015-07-15' and '2015-07-25' 

给你一个观点

date        |standardPrices|summerPrices|winterPrices|
------------------------------------------------------
2015-07-15  |       100    |     0      |     0      |
2015-07-16  |       100    |     0      |     0      |
2015-07-17  |       100    |     0      |     0      |
2015-07-18  |       100    |     0      |     0      |
2015-07-19  |       100    |     0      |     0      |
2015-07-20  |       100    |     0      |     0      |
2015-07-20  |       0      |     150    |     0      |
2015-07-21  |       100    |     0      |     0      |
2015-07-21  |       0      |     150    |     0      |

如果您然后按“日期”对该视图进行分组;您可以反过来将其用于 运行 查询以聚合(再次使用 'if (summerPrices > 0, summerPrices, standardPrices)

这不是最优雅的解决方案,但它确实有效,而且速度快如闪电,让您可以在查询中使用联接而不是循环,我更喜欢后者。

我会使用 table 个日期:一个 table (datelist),它由一列 (date) 组成,并且包含之前和之前的所有日期即将 n 年。

查询的粗略概述(您可能需要更正结束日期):

SELECT
    datelist.date AS Night,
    IFNULL(seasonal_rates.Range_Name, standard_rates.Range_Name) AS Season,
    IFNULL(seasonal_rates.Rate, standard_rates.Rate) AS Rate
FROM datelist
LEFT JOIN rates AS standard_rates ON standard_rates.Range_Name = 'Standard'
LEFT JOIN rates AS seasonal_rates ON datelist.date BETWEEN seasonal_rates.Start_Date AND seasonal_rates.End_Date
WHERE standard_rates.RoomId = 46
AND   seasonal_rates.RoomId = 46
AND   datelist.date BETWEEN '2015-07-15' AND '2015-07-25'

然后您可以将结果传递到 SUM/GROUP BY 查询中。