MYSQL:如何选择两个日期之间的每个YEAR_MONTH?

时间:2015-01-15 17:17:42

标签: mysql

我想做什么:

我有一张这样的表:

TABLE mytable
- ID (INT)
- START (DATETIME)
- END (DATETIME)

假设我有这些行:

| ID  |         START       |          END        |
|--------------------------------------------------
|  1  | 2014-01-02 00:00:00 | 2014-12-02 00:00:00 | => month between : 12
|  2  | 2014-01-03 00:00:00 | 2015-02-03 00:00:00 | => month between : 14

注意:“月之间”包括开始和结束月份

我在START和END之间的每个YEAR_MONTH,我想显示如下一行:

ID  |  MONTH  |  YEAR
---------------------
1   |    1    |  2014
1   |    2    |  2014
1   |    3    |  2014
1   |    4    |  2014
1   |    5    |  2014
1   |    6    |  2014
1   |    7    |  2014
1   |    8    |  2014
1   |    9    |  2014
1   |    10   |  2014
1   |    11   |  2014
1   |    12   |  2014
2   |    1    |  2014
2   |    2    |  2014
2   |    3    |  2014
2   |    4    |  2014
2   |    5    |  2014
2   |    6    |  2014
2   |    7    |  2014
2   |    8    |  2014
2   |    9    |  2014
2   |    10   |  2014
2   |    11   |  2014
2   |    12   |  2014
2   |    1    |  2015
2   |    2    |  2015

ID为1的12条记录,ID 2为14条。

当月数是>时我有点卡住了12

我在哪里:

我这样做:

SELECT mytable.id,
months.id as month,
YEAR(start) as year
FROM mytable
/* Join on a list from 1 to 12 */
LEFT JOIN (SELECT 1 as id UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12)
as months ON months.id BETWEEN MONTH(start) AND MONTH(end) 
order by mytable.id, month, year

因此,对于第1个月和第2个,ID 2只有2行:

ID  |  MONTH  |  YEAR
---------------------
1   |   1     |   2014
1   |   2     |   2014
1   |   3     |   2014
1   |   4     |   2014
1   |   5     |   2014
1   |   6     |   2014
1   |   7     |   2014
1   |   8     |   2014
1   |   9     |   2014
1   |   10    |   2014
1   |   11    |   2014
1   |   12    |   2014
2   |   1     |   2014
2   |   2     |   2014

您对此问题有任何想法或建议吗? 有没有办法在两个日期之间提取每个YEAR_MONTH? 谢谢。

HELPER:

这是一个创建表并插入提到的2行的脚本:

CREATE TABLE mytable (
    id INT PRIMARY KEY auto_increment,
    start DATETIME NOT NULL,
    end DATETIME NOT NULL
);

INSERT INTO mytable (start,end) VALUES 
("2014-01-02 00:00:00","2014-12-02 00:00:00"),
("2014-01-03 00:00:00","2015-02-03 00:00:00");

3 个答案:

答案 0 :(得分:1)

如果我理解正确,您需要一个表格,其中包含每个开始日期和结束日期之间的日期(年 - 月)。

没有简单的select语句可以为您提供此功能,但您可以创建一个程序来执行此操作。您需要创建一个临时表,用您需要的值填充它,然后输出结果。

这是我提出的解决方案(考虑永久性表格):

SQL Fiddle

MySQL 5.5.32架构设置

CREATE TABLE mytable (
    id INT PRIMARY KEY auto_increment,
    start DATETIME NOT NULL,
    end DATETIME NOT NULL
)//

INSERT INTO mytable (start,end) VALUES 
("2014-01-02 00:00:00","2014-12-02 00:00:00"),
("2014-01-03 00:00:00","2015-02-03 00:00:00")//

create procedure year_month_table()
begin
  -- Declare the variables to fill the years_months table
  declare id int;
  declare start_date, end_date, d date;
  -- Declare the "done" variable for the loop that fills the table,
  -- the cursor to read the data, and the handler to check if the
  -- loop should end.
  declare done int default false;
  declare cur_mytable cursor for
    select * from mytable;
  declare continue handler for not found
    set done = true;
  -- Create the table to hold your data
  create table if not exists years_months (
    row_id int unsigned not null auto_increment primary key,
    id int not null,
    month int,
    year int,
    unique index dedup(id, year, month),
    index idx_id(id),
    index idx_year(year),
    index idx_month(month)
  );
  -- Open the cursor to read the ids and the start and end dates for each one
  open cur_mytable;
  -- Disable the indexes to speed up insertion
  alter table years_months disable keys;
  -- Start the loop
  loop_data: loop
    -- Read the values from your table and store them in the variables
    fetch cur_mytable into id, start_date, end_date;
    -- If you've reached the end of the table, then you must exit the loop
    if done then
      leave loop_data;
    end if;
    -- Initialize the date to fill the table
    set d = start_date;
    while d <= end_date do
      -- Insert the values in your table
      insert ignore into years_months (id, month, year) values (id, month(d), year(d));
      -- Increment the d variable in 1 month
      set d = date_add(d, interval +1 month);
    end while;
  end loop;
  close cur_mytable;
  -- Enable the indexes again
  alter table years_months enable keys;
  -- Show the result
  select * from years_months;
end //

查询1

select * from mytable

<强> Results

| ID |                          START |                             END |
|----|--------------------------------|---------------------------------|
|  1 | January, 02 2014 00:00:00+0000 | December, 02 2014 00:00:00+0000 |
|  2 | January, 03 2014 00:00:00+0000 | February, 03 2015 00:00:00+0000 |

查询2

call year_month_table()

<强> Results

| ROW_ID | ID | MONTH | YEAR |
|--------|----|-------|------|
|      1 |  1 |     1 | 2014 |
|      2 |  1 |     2 | 2014 |
|      3 |  1 |     3 | 2014 |
|      4 |  1 |     4 | 2014 |
|      5 |  1 |     5 | 2014 |
|      6 |  1 |     6 | 2014 |
|      7 |  1 |     7 | 2014 |
|      8 |  1 |     8 | 2014 |
|      9 |  1 |     9 | 2014 |
|     10 |  1 |    10 | 2014 |
|     11 |  1 |    11 | 2014 |
|     12 |  1 |    12 | 2014 |
|     13 |  2 |     1 | 2014 |
|     14 |  2 |     2 | 2014 |
|     15 |  2 |     3 | 2014 |
|     16 |  2 |     4 | 2014 |
|     17 |  2 |     5 | 2014 |
|     18 |  2 |     6 | 2014 |
|     19 |  2 |     7 | 2014 |
|     20 |  2 |     8 | 2014 |
|     21 |  2 |     9 | 2014 |
|     22 |  2 |    10 | 2014 |
|     23 |  2 |    11 | 2014 |
|     24 |  2 |    12 | 2014 |
|     25 |  2 |     1 | 2015 |
|     26 |  2 |     2 | 2015 |

请注意,过程中的最后一个select语句是输出结果的语句。您可以在每次需要时执行它。

希望这有帮助

答案 1 :(得分:0)

我找到了另一种方法,但这不是一个简单的select语句,我认为它很容易出错,但无论如何我会把它放在这里:

select mytable.id, month, year
from mytable, 
(select month, year
from 
  (select 1 as month 
  union select 2 
  union select 3
  union select 4
  union select 5
  union select 6
  union select 7
  union select 8
  union select 9
  union select 10
  union select 11
  union select 12) as a, 
  (select year(start) as year from mytable
  union select year(end) as year from mytable) as b) as a
where cast(concat_ws('-', a.year, a.month, day(mytable.start)) as date) 
      between date(mytable.start) and date(mytable.end)
order by mytable.id, year, month;

查看此其他SQL fiddle

答案 2 :(得分:0)

我知道我参加晚会很晚,但是我需要一个好的解决方案,而排序不适用于我的数据库版本。

我从https://stackoverflow.com/a/14813173/1707323开始,进行了一些更改以使其可以在本OP中使用。

SELECT
  DATE_FORMAT(m1, '%c') AS month_single,
  DATE_FORMAT(m1, '%Y') AS this_year
FROM
 (
  SELECT 
   '2017-08-15' +INTERVAL m MONTH AS m1
  FROM
   (
     SELECT
       @rownum:=@rownum+1 AS m
     from
      (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t1,
      (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t2,
      (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t3,
      (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t4,
      (SELECT @rownum:=-1) t0
   ) d1
 ) d2 
WHERE
  m1<='2020-03-23'
ORDER BY m1

这将获得这两个日期之间的所有月份。请注意,开始日期在第二个select子句中,结束日期在最后的where子句中。这也将包括开始月份和结束月份。可以轻松地对其进行修改,以排除一些额外的+/-间隔的开始和结束月份。