简体   繁体   中英

MySQL SELECT TMP rows - DATES between Start and End date

My table contains the following structure:

START_DATE | END_DATE   | COST
2012-11-01 | 2012-11-05 | 500.5

I would like to perform a SELECT statement that returns the following result:

DATE       | COST
2012-11-01 | 100.1
2012-11-02 | 100.1
2012-11-03 | 100.1
2012-11-04 | 100.1
2012-11-05 | 100.1

I can figure out how to divide the total cost by the amount of days between start and end date, but no idea how to create these 'virtual rows' for the DATE column.

Maybe it isn't possible at all. Any help is very much appreciated!

Your best bet is to create a Calendar table.

For your purposes, all you need is one column...

CREATE TABLE Calendar AS (calendar_date DATE)

Then you fill that table with all the dates you will ever be interested in. Even if it's a hundred years, the table will still be tiny.

Do remember to add that column as a Primary Key.

Then you just join on the table...

SELECT
  *
FROM
  yourTable
INNER JOIN
  calendar
    ON  calendar.calendar_date >= yourTable.start_date
    AND calendar.calendar_date <= yourTable.end_date

Later you may find that you want to know dates of holidays, financial year boundaries, etc. These can all be added as addition fields and indexes to that calendar table.

It's much like a cache, rather than re-calculating messy date based rules within a query.

Or, you can think of it as a dimension table where the data is the ID and the other columns are facts.

I agree with Dems to have a calender table, which is the best option.. As Mysql doesn't have a function like ROW_NUMBER as many other RDBMSs have.

Another option would be to create a table with sequence number. You can populate this table with a considerable number of values (say 1 through 100k), which you think will be the maximum number of days between any start_date and end_date

something like

create table seq

(rn int);

insert into seq
select 0 as rn union all
select 1 as rn union all
select 2 as rn union all
select 3 as rn union all
select 4 as rn union all
select 5 as rn 

then run following query:

select DATE_ADD(START_DATE, INTERVAL rn day) ,
       COST/(select datediff(END_DATE,START_DATE)+1 from table1)
from Table1
cross join seq
where rn<=(select datediff(END_DATE,START_DATE) from table1)


SQL fiddle demo

i agree with you Sebas that this one works in oracle.. Place your tablename in place of table1

if you don't have access to all_objects then

select ( I +START_DATE-1) date1,cost/((END_DATE-START_DATE)+1) cost from table1 ,(     select I  from DUAL
              model
              dimension by (1 i)
              measures (0 X)
              (X[for I from 2 to 10000 increment 1] = 0)) 
              where END_DATE>=I +START_DATE-1;

if you have access to metatable all_objects then try this one

SELECT DATE1.DATE_ALL, table1.cost/((table1.END_DATE-table1.START_DATE)+1) NEW_COST 
FROM table1 INNER JOIN 
    (
     SELECT START_DATE + rownum-1 DATE_ALL 
     FROM ALL_OBJECTS
             CROSS JOIN table1
     WHERE 
         START_DATE + rownum - 1 <= (SELECT MAX(END_DATE) FROM table1)
    ) DATE1 ON table1.END_DATE >= DATE1.DATE_ALL

please let me know if you have any issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM