如何查找给定日期范围内的确切季节和日期?

时间:2011-08-20 19:31:12

标签: php mysql date-range

我需要租用汽车价格计算。汽车价格因季节而异。

我有一个像这样的season_dates表

id    slug    start                  end 
1     low     2011-01-01 00:00:00    2011-04-30 00:00:00
2     mid     2011-05-01 00:00:00    2011-06-30 00:00:00
3     high    2011-07-01 00:00:00    2011-08-31 00:00:00
4     mid     2011-09-01 00:00:00    2011-10-31 00:00:00
5     low     2011-11-01 00:00:00    2011-12-31 00:00:00

用户选择日期,例如:

start_day   08/20   end_day  08/25

我的查询:

SELECT * from arac_donemler
where DATE_FORMAT(start, '%m/%d') <= '08/20'
  and DATE_FORMAT(end, '%m/%d') >= '08/25'

这给了我正确的旺季。

但我无法处理的是:如果用户选择2个季节之间的日期范围会怎么样?

例如,从8月20日到9月5日。

这次我必须发现日期范围属于哪个季节? 我必须计算每个季节的天数?

对于上面的例子, 旺季截至8月31日。所以31-20 =旺季11天,中期5天。

我如何提供这种分离?

我希望我能解释一下。

我尝试了很多东西,比如连接表,但是没能成功。

3 个答案:

答案 0 :(得分:0)

我会让其他人以正确的方式在SQL中进行日期比较(你几乎肯定会杀死表格的索引),但是一开始,你可以准确地得到相关的季节 by

select * from arac_donemler
 where end >= [arrival-date]
   and start <= [departure-date]

然后,您应该在业务逻辑中而不是在数据库查询中完成剩余的处理(计算每个季节中的天数等等)。

答案 1 :(得分:0)

我会在表格中存储所有单日。 这是一个简单的例子。

create table dates (
id int not null auto_increment primary key,
pday date,
slug tinyint,
price int);

insert into dates (pday,slug,price)
values 
('2011-01-01',1,10),
('2011-01-02',1,10),
('2011-01-03',2,20),
('2011-01-04',2,20),
('2011-01-05',2,20),
('2011-01-06',3,30),
('2011-01-07',3,30),
('2011-01-08',3,30);

select 
concat(min(pday),'/',max(pday)) as period,
count(*) as days,
sum(price) as price_per_period
from dates 
where pday between '2011-01-02' and '2011-01-07'
group by slug

+-----------------------+------+------------------+
| period                | days | price_per_period |
+-----------------------+------+------------------+
| 2011-01-02/2011-01-02 |    1 |               10 |
| 2011-01-03/2011-01-05 |    3 |               60 |
| 2011-01-06/2011-01-07 |    2 |               60 |
+-----------------------+------+------------------+
3 rows in set (0.00 sec)

编辑。版本为grandtotal

select 
case
when slug is null then 'Total' else concat(min(pday),'/',max(pday)) end as period,
count(*) as days,
sum(price) as price_per_period
from dates 
where pday between '2011-01-02' and '2011-01-07'
group by slug
with rollup;

+-----------------------+------+------------------+
| period                | days | price_per_period |
+-----------------------+------+------------------+
| 2011-01-02/2011-01-02 |    1 |               10 |
| 2011-01-03/2011-01-05 |    3 |               60 |
| 2011-01-06/2011-01-07 |    2 |               60 |
| Total                 |    6 |              130 |
+-----------------------+------+------------------+
4 rows in set (0.00 sec)

修改。填充表

的存储过程
delimiter $$
create procedure calendario(in anno int)
begin
  declare i,ultimo int;
  declare miadata date; 
  set i = 0;
  select dayofyear(concat(anno,'-12-31')) into ultimo;
  while i < ultimo do
    select concat(anno,'-01-01') + interval i day into miadata;
    insert into dates (pday) values (miadata);
    set i = i + 1;
  end while;
end $$
delimiter ;

call calendario(2011);

答案 2 :(得分:0)

如果你也有一张桌子出租(真正的版本需要很多其他细节):

CREATE TABLE Rental
(
    start   DATE NOT NULL,
    end     DATE NOT NULL
);

并用以下内容填充:

INSERT INTO rental VALUES('2011-08-20', '2011-09-05');
INSERT INTO rental VALUES('2011-08-20', '2011-08-25');

然后此查询产生合理的结果:

SELECT  r.start AS r_start, r.end AS r_end,
        s.start AS s_start, s.end AS s_end,
        GREATEST(r.start, s.start) AS p_start,
        LEAST(r.end, s.end)        AS p_end,
        DATEDIFF(LEAST(r.end, s.end), GREATEST(r.start, s.start)) + 1 AS days,
        s.id, s.slug
  FROM rental AS r
  JOIN season_dates AS s ON r.start <= s.end AND r.end >= s.start;

它产生:

r_start     r_end       s_start     s_end       p_start     p_end       days id slug
2011-08-20  2011-09-05  2011-07-01  2011-08-31  2011-08-20  2011-08-31  12   3  high
2011-08-20  2011-09-05  2011-09-01  2011-10-31  2011-09-01  2011-09-05   5   4  mid 
2011-08-20  2011-08-25  2011-07-01  2011-08-31  2011-08-20  2011-08-25   6   3  high

请注意,我计算的是12天而不是11天;这是+1表达式中的days。这很棘手;你必须决定是否在租用的同一天归还汽车,这是一天的租车吗?如果第二天返回怎么办?也许时间很重要?但这涉及详细的业务规则而不是一般原则。也许持续时间是原始DATEDIFF()和1的较大者?另请注意,此架构中只有租赁开始和结束日期用于识别租赁;真实的架构在租赁表中会有某种租赁协议编号。

(忏悔:在MacOS X 10.7.1上使用IBM Informix 11.70.FC2进行模拟,但MySQL被记录为支持LEAST,GREATEST和DATEDIFF,我在Informix中模拟了这些。最显着的区别可能是Informix有DATE没有任何时间组件的类型,所以没有时间需要或显示。)


  

但[...]季节期间每年都相同。所以我想只比较几天和几个月。 2011年并不重要。明年将使用2011年。出现这个问题。例如淡季包括11月,12月,然后是1月,2月,3月,4月。如果用户选择日期范围01.05.2011到... 2011没有问题。我只是将月和日与DATE_FORMAT进行比较(结束,'%m /%d')。但如果他选择从12月到明年1月的范围,我将如何计算天数?

请注意,Season_Dates表中每年有5个条目不会使8英寸的磁盘破坏存储容量好几年,更不用说500 GiB的怪物磁盘。所以,到目前为止最简单的事情是在Season_Dates表中的5个新行中定义2012年的条目。这也允许您处理这样一个事实,即在12月,决定规则的权力将是不同的(12月20日至1月4日将是'中期' ,而不是“低”季节,例如)。