【问题标题】:Calculate price given standard rates and seasonal date ranges计算给定标准费率和季节性日期范围的价格
【发布时间】:2015-03-11 14:20:21
【问题描述】:

我正在构建一个预订风格的系统。房间有一个年度标准价格(这是我的问题与其他类似问题不同的地方,因为其他房间有独特的范围)然后它们也有一年内的日期范围,价格不同。我正在寻找在 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,我想要这样计算:

  • 2015-07-15 ... 2015-07-19 按标准价格(每晚 100 元)
  • 2015-07-20 ... 2015-07-24 夏季价格(每晚 150)

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

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

编辑

我应该补充一下,我希望在 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

【问题讨论】:

  • 我认为最好的方法是用光标(从 startDate 到 endDate)循环每一天,并将费率添加到每个日期的总和中。否则,您需要将其拆分为 N 个案例,其中 N 是未知的。
  • 我希望在 PHP 中执行此操作,这可能吗?
  • 这可能是更好的路线(更容易)。同样的概念也适用。您需要一个循环在日期范围内的每一天进行迭代。然后选择每个日期的价格并添加到总和中。
  • “在 PHP 中循环并为每一天执行一次查询” - 查询太多。最好通过一次查询从数据库中获取所有选项,并通过在 PHP 中进行比较来获取价格。始终尽可能少地执行数据库查询。

标签: php mysql sql datetime date-range


【解决方案1】:

我会使用一个日期表:一个表 (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 查询。

【讨论】:

  • 最后决定走这条路。 SQL 要简单得多,我设法通过使用它解决了我在原始查询中遇到的问题。我已经更新了我的问题以包括我的最终工作查询。谢谢!
【解决方案2】:

使用 MySQL CASE 语句。

例如

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

【讨论】:

  • 如果您编辑您的问题并将其放入您的表格结构中,很容易弄清楚,您是否使用带有参数的存储存储过程来返回上述结果?请澄清。谢谢
  • 该表非常简单,仅包含我示例中的数据:姓名、开始日期、结束日期、房间 ID 和费率。没有存储过程或类似的东西,只有位于 mysql 表中的表,用户通过 PHP 站点输入费率。我要做的是使用该数据,根据日期范围计算总费率
【解决方案3】:

我之前处理这类问题的方法是建立一个“日期”表。

1990 年 1 月 1 日 .. 2050 年 1 月 1 日(您可以一次完成,但为了几 Mb 的存储空间,“真实”表的性能提升值得设置一次。

例如。

|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      |

如果您 THEN 按“日期”分组此视图;您可以反过来使用它来运行查询以进行聚合(再次使用 'if (summerPrices > 0, summerPrices, standardPrices)

不是最优雅的解决方案,但它可以工作,而且速度极快,让您可以使用联接而不是在查询中循环,我更喜欢这种方式。

【讨论】:

  • 您绝对不需要日期 ID。日期几乎是不变的、独特的、不变的。
  • 我不设置日期范围,它们完全灵活,取决于所有者为每个设置的内容,所以我不能像这样对它们进行分组。他们甚至可能不会费心设置自定义时间段,我只能保证会有一年的标准价格和每天最多一个替代价格。鉴于此,如果我沿着这条日期表加入路线走,如果有任何日期的自定义日期范围,我将如何优先考虑自定义日期范围?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多