Daily Report in a month from different tables - mysql

I have four Tables with four date coloumns respectively.
Table 1 ---------- Date 1
Table 2 ---------- Date 2
Table 3 ---------- Date 3
Table 4 ---------- Date 4
Now i want to get a day report in a month for all the four tables.if there is no data in any particular table for particular date it should dispaly NULL.How can i achieve it?
Structure:-
Table-1:-
ID Amount1 Date1
1 340 24/04/2013
2 200 04/04/2013
3 1000 15/04/2013
Table-2:-
ID Amount2 Date2
1 2000 22/04/2013
2 200 04/04/2013
3 1500 15/04/2013
Table-3:-
ID Amount3 Date3
1 3400 24/04/2013
2 200 19/04/2013
3 1800 15/04/2013
Table-4:-
ID Amount4 Date4
1 3200 24/04/2013
2 2200 04/04/2013
3 1000 18/04/2013
Now my result should be like
Date Amount1 Amount2 Amount3 Amount4
01/04/2013 Null Null Null Null
|
|
|
04/04/2013 200 200 null 2200
|
|
|
|
15/0402013 1000 1500 1800 null
|
|
|
|
|24/0402013 340 null 3400 3200
|
|
|
|
31/04/2013

Using a subquery to get a range of dates (I am assuming you want every day in April 2013) and then left joining that against the tables of data.
SELECT, dates.aDate, Table-1.Amount1, Table-2.Amount2, Table-3.Amount3, Table-4.Amount4
FROM
(
SELECT DATE_ADD('2013-04-01', INTERVAL (Units.i + Tens.i * 10) DAY) AS aDate
FROM
(SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) Units,
(SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) Tens
HAVING aDate <= '2013-04-30'
) dates
LEFT OUTER JOIN Table-1 ON Table-1.Date1 = dates.aDate
LEFT OUTER JOIN Table-2 ON Table-2.Date2 = dates.aDate
LEFT OUTER JOIN Table-3 ON Table-3.Date3 = dates.aDate
LEFT OUTER JOIN Table-4 ON Table-4.Date4 = dates.aDate
This assumes that there are not duplicate dates in any particular table.

You can try the following query
with dates as (
(select date from date1)union(select date from date2)union
(select date from date3)union (select date from date4) order by date asc)
select date,
(select amount from date1 where date=dt.date limit 1),
(select amount from date2 where date=dt.date limit 1),
(select amount from date3 where date=dt.date limit 1),
(select amount from date4 where date=dt.date limit 1)
from dates as dt;
You can add the date constraints on dates.
P.S.: Tested on PgSQL

Related

List dates from specific mysql table and return 0 if day does not exists

I'm trying to generate a result from a query that list the last 7 days from today (2020/07/15) and the views matching a specific code.
If in that day the code has no views, I want the day to return 0.
Table Format
DAY | CODE | VIEWS
2020-07-10 | 123 | 5
2020-07-11 | 123 | 2
2020-07-12 | 123 | 3
2020-07-15 | 123 | 8
2020-07-15 | 124 | 2
2020-07-15 | 125 | 2
Expected result from code 123
DAY | VIEWS
2020-07-09 | 0
2020-07-10 | 5
2020-07-11 | 2
2020-07-12 | 3
2020-07-13 | 0
2020-07-14 | 0
2020-07-15 | 8
I already found a way to generate the calendar dates from here and adjust to my needs, but I don't know how to join the result with my table.
select * from
(select
adddate(NOW() - INTERVAL 7 DAY, t0) day
from
(select 1 t0
union select 1
union select 2
union select 3
union select 4
union select 5
union select 6
union select 7) t0) v
Any help would by apreceated.
One option uses a recursive query - available in MySQL 8.0:
with recursive cte as (
select current_date - interval 6 day dt
union all
select dt + interval 1 day from cte where dt < current_date
)
select c.dt, coalesce(sum(t.views), 0) views
from cte
left join mytable t on t.day = c.dt
group by c.dt
order by c.dt
You can also manually build a derived table, as you originaly intended to (this would work on all versions of MySQL):
select current_date - interval d.n day dt, coalesce(sum(t.views), 0) views
from (
select 0 n
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
) d
left join mytable t on t.day = current_date - interval d.n day
group by d.n
order by d.n desc

Group query by date which is between range

I have a table like:
id | date_start | date_end
1 | 2019.05.01 | 2019.05.06
2 | 2019.05.05 | 2019.05.05
3 | 2019.05.05 | 2019.05.08
Date_start and date_end means that entry is active during this period. Is it possible to get days with his count of active entries for intermediate values too? Like:
2019.05.01 | 1
2019.05.02 | 1
2019.05.03 | 1
2019.05.04 | 1
2019.05.05 | 3
2019.05.06 | 2
2019.05.07 | 1
2019.05.08 | 1
I think that this will work for you:
SELECT temp_date,
COUNT(*)
FROM (
SELECT ((SELECT MAX(date_start) FROM {dates_table}) - INTERVAL c.number DAY) AS temp_date
FROM (SELECT singles + tens + hundreds number
FROM ( SELECT 0 singles
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) singles
JOIN (SELECT 0 tens
UNION ALL SELECT 10 UNION ALL SELECT 20 UNION ALL SELECT 30
UNION ALL SELECT 40 UNION ALL SELECT 50 UNION ALL SELECT 60
UNION ALL SELECT 70 UNION ALL SELECT 80 UNION ALL SELECT 90
) tens
JOIN (SELECT 0 hundreds
UNION ALL SELECT 100 UNION ALL SELECT 200 UNION ALL SELECT 300
UNION ALL SELECT 400 UNION ALL SELECT 500 UNION ALL SELECT 600
UNION ALL SELECT 700 UNION ALL SELECT 800 UNION ALL SELECT 900
) hundreds
ORDER BY number DESC) c
WHERE c.number BETWEEN 0 AND (SELECT DATEDIFF(MAX(date_end),MIN(date_start)) FROM {dates_table})
) calendar
JOIN {dates_table} ON temp_date BETWEEN end_date AND start_date
GROUP BY temp_date
Just replace {dates_table} with the name of your table.
The base of this that generates the series of dates was taken from IvanD's answer on this SO question: How to populate a table with a range of dates?

How to fetch data between two column of date type

I have a table for storing events data, that is containing following two entries
id | title | startdate | enddate
1 | event1 | 2019-04-01 | 2019-04-04
2 | event2 | 2019-04-04 | 2019-04-05
I need to write a query to fetch the data as
id| title | date
1 | event1 | 2019-04-01
1 | event1 | 2019-04-02
1 | event1 | 2019-04-03
1 | event1 | 2019-04-04
1 | event2 | 2019-04-04
1 | event2 | 2019-04-05
Please suggest any query.
Mysql query please.
Solution of your problem can be solved by creating a table having dates of around 273 years.
Below query creates the table:
CREATE TABLE test2 AS
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9);
CREATE TABLE calendar AS
select adddate('1900-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) sdate
from test2 t0,test2 t1,test2 t2,test2 t3,test2 t4;
Range of Calendar Table which you can change and can increase also:
MIN(sdate) MAX(sdate)
1900-01-01 2173-10-15
Using The above table you can generate dates between the required range. Below query gives your solution:
SELECT id,title,c.sdate as "date"
FROM test t
INNER JOIN calendar c
ON c.sdate BETWEEN t.startdate AND t.enddate;
Output:
id title date
1 event1 2019-04-01
1 event1 2019-04-02
1 event1 2019-04-03
1 event1 2019-04-04
2 event2 2019-04-04
2 event2 2019-04-05
For demo follow below link:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=6ebd669acae67d23cb5b6b9d04c86a10
You can join two column into one.. there is no such method like that..
SELECT id,title,concat(startdate ,", ",enddate) as date FROM `table`
Result would be
id| title | date
1 | event2 | 2019-04-04, 2019-04-05
2 | event1 | 2019-04-01, 2019-04-02
Now you can separate "date" into two string/column by php or whatever the language is..
You can do without creating table. This query below is to make a date range without creating or referring to any table:
SELECT 1 as 'tempid',CONCAT_WS('-','2019-04',LPAD((a+b),2,0)) AS 'dates'
FROM
(SELECT 0 a UNION
SELECT 1 UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9) a,
(SELECT 0 b UNION
SELECT 10 UNION
SELECT 20 UNION
SELECT 30) dd;
Note that if you run the query above, you'll get date range between 2019-04-00 and 2019-04-39.
SELECT t.id,t.title,d.dates FROM
(SELECT 1 as 'tempid',CONCAT_WS('-','2019-04',LPAD((a+b),2,0)) AS 'dates'
FROM
(SELECT 0 a UNION
SELECT 1 UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9) a,
(SELECT 0 b UNION
SELECT 10 UNION
SELECT 20 UNION
SELECT 30) d) d LEFT JOIN
-- this part here is your query from the table
(SELECT id,title,startdate,enddate,1 AS tempid
FROM your_table) t
ON d.tempid=t.tempid WHERE d.dates BETWEEN startdate AND enddate;
EDIT: If you want whole year of dates without creating table, you can use this query:
SELECT CONCAT_WS('-',yy,LPAD(m,2,0),LPAD(days,2,0)) dates FROM
(SELECT 1 AS id,a+b AS 'days' FROM
(SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
UNION SELECT 8 UNION SELECT 9) a,
(SELECT 0 b UNION SELECT 10 UNION SELECT 20 UNION SELECT 30) b) d LEFT JOIN
(SELECT 1 AS id,m FROM
(SELECT 1 m 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) m) m ON d.id=m.id LEFT JOIN
-- This part here > YEAR(CURDATE())=2019.
(SELECT 1 AS id, YEAR(CURDATE() - INTERVAL 1 YEAR) yy
-- Adding '- INTERVAL 1 YEAR' means that it will reduce 1 year or YEAR(CURDATE()).
-- Hence it will become '2018'.
-- If you want future year, just change it to '+ INTERVAL 1 YEAR' = 2020 or '+ INTERVAL 2 YEAR' = 2021.
) yy ON d.id=yy.id WHERE days BETWEEN 1 AND 31 ORDER BY dates;
Please take note that this is just a temporary table use for checking as the dates here will all be between 1 and 31. It means that the last date for every month is 201X-XX-31 regardless if it's February or June. The main purpose here is to use this for a quick comparison only. There are ways to make sure that all these dates will follow the correct date in the calendar but the query will be complex.

MySQL: count unique days in period with overlaps and gaps (optimized)

How to calculate the number of unique days per month from a table with two date columns, in which the periods can have gaps and overlaps?
I rather not use a calendar table to get the unique days, because it generates a temporary table with thousands of records, and resources are limited.
Example table:
+---------+------------+------------+
| mygroup | alpha | omega |
+---------+------------+------------+
| 1 | 2017-02-04 | 2017-04-14 |
| 1 | 2017-03-25 | 2017-03-28 |
| 1 | 2017-01-23 | 2017-01-25 |
| 2 | 2017-02-05 | 2017-02-20 |
| 1 | 2017-04-28 | 2017-05-12 |
| etc. | etc. | etc. |
+---------+------------+------------+
Is it what you need?
select count(distinct selected_date),te.mygroup, MONTHNAME(selected_date)from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) selected_date from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
cross join test te
where selected_date between te.alpha and te.omega
group by mygroup, MONTHNAME(selected_date)
Оutput for your example:
'17','1','April'
'25','1','February'
'3','1','January'
'31','1','March'
'12','1','May'
'16','2','February'
Count may be greater than number of the days in month because such overlap exists in few rows - it's not а mistake.
There is another way to do this roughly 10 times faster than a calendar table.
The biggest resource spoiler is the calendar table itself, it is used to filter unique days.
But instead of using a whole table record for that, it can be done using 31 bits in an UINT.
Recepe:
Create a calendar table with months only
Cut periods in months, and join them with the calendar table
Translate periods to bits of UINTs
OR the UINTs per month for uniqueness
Count their bits as unique days per month
Output:
+--------------+---------+---------+-------+
| Period | Group 1 | Group 2 | Total |
+--------------+---------+---------+-------+
| 2017 month 5 | 11 | 0 | 11 |
| 2017 month 4 | 15 | 0 | 15 |
| 2017 month 3 | 30 | 0 | 30 |
| 2017 month 2 | 24 | 15 | 39 |
| 2017 month 1 | 2 | 0 | 2 |
+--------------+---------+---------+-------+
MySQL query:
SELECT
`tabulate`.`period` AS `Period`,
SUM(IF(`tabulate`.`mygroup` = 1,
`tabulate`.`days`, 0)) AS `Group 1`,
SUM(IF(`tabulate`.`mygroup` = 2,
`tabulate`.`days`, 0)) AS `Group 2`,
SUM(`tabulate`.`days`) AS `Total`
FROM
( SELECT
`unique`.`period`,
BIT_COUNT(BIT_OR(CONV(CONCAT(
REPEAT("1", DAYOFMONTH(`unique`.`omega`) - DAYOFMONTH(`unique`.`alpha`)),
REPEAT("0", DAYOFMONTH(`unique`.`alpha`) - 1)
), 2, 10))) AS `days`,
`unique`.`mygroup`
FROM
( SELECT
DATE_FORMAT(`permonth`.`period_alpha`, "%Y month %c") AS `period`,
GREATEST(`permonth`.`period_alpha`, `permonth`.`example_alpha`) AS `alpha`,
LEAST(`permonth`.`period_omega`, `permonth`.`example_omega`) AS `omega`,
`permonth`.`mygroup`
FROM
( SELECT
`period`.`alpha` AS `period_alpha`,
DATE_SUB(`period`.`omega`, INTERVAL 1 DAY) AS `period_omega`,
`example`.`mygroup`,
IFNULL(`example`.`alpha`, `period`.`alpha`) AS `example_alpha`,
IFNULL(`example`.`omega`, CURDATE()) AS `example_omega`
FROM
( SELECT
DATE_ADD(
MAKEDATE(YEAR(CURDATE()), 1),
INTERVAL `season`.`n` + (`month`.`n` << 2) MONTH
) AS `alpha`,
DATE_ADD(
MAKEDATE(YEAR(CURDATE()), 1),
INTERVAL 1 + `season`.`n` + (`month`.`n` << 2) MONTH
) AS `omega`
FROM
( SELECT 0 AS `n`
UNION ALL SELECT 1
UNION ALL SELECT 2
) AS `month`
CROSS JOIN (SELECT 0 AS `n`
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
) AS `season`
) AS `period`
INNER JOIN
( SELECT 1 AS `mygroup`, "2017-02-04" AS `alpha`, "2017-04-14" AS `omega`
UNION ALL SELECT 1, "2017-03-25", "2017-03-28"
UNION ALL SELECT 1, "2017-01-23", "2017-01-25"
UNION ALL SELECT 2, "2017-02-05", "2017-02-20"
UNION ALL SELECT 1, "2017-04-28", "2017-05-12"
) AS `example` ON (
(`example`.`alpha` < `period`.`omega` OR `example`.`alpha` IS NULL)
AND IFNULL(`example`.`omega`, CURDATE()) >= `period`.`alpha`
)
) AS `permonth`
) AS `unique`
GROUP BY
`unique`.`period`,
`unique`.`mygroup`
) AS `tabulate`
GROUP BY `tabulate`.`period`
ORDER BY `tabulate`.`period` DESC

MySql Single Table, Select last 7 days and include empty rows

I have searched similar problems here on stackoverflow but I could not understand how to make this work, what I'm trying to do...
So, I want to get last 7 days transactions from database and get total sales amount and also include empty rows if there is no data for some day.
What I have so far:
http://sqlfiddle.com/#!2/f4eda/6
This outputs:
| PURCHASE_DATE | AMOUNT |
|---------------|--------|
| 2014-04-25 | 19 |
| 2014-04-24 | 38 |
| 2014-04-22 | 19 |
| 2014-04-19 | 19 |
What I want:
| PURCHASE_DATE | AMOUNT |
|---------------|--------|
| 2014-04-25 | 19 |
| 2014-04-24 | 38 |
| 2014-04-23 | 0 |
| 2014-04-22 | 19 |
| 2014-04-21 | 0 |
| 2014-04-20 | 0 |
| 2014-04-19 | 19 |
Any help appreciated :)
This is not easy. I took help from this thread generate days from date range and combined it with your query.
So the idea was to get the list of dates from last 7 days then left join these dates with a static amount 0 to the query you have and then finally sum them. This could be used for any date range, just need to change them in both the queries
select
t1.purchase_date,
coalesce(SUM(t1.amount+t2.amount), 0) AS amount
from
(
select DATE_FORMAT(a.Date,'%Y-%m-%d') as purchase_date,
'0' as amount
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) a
where a.Date BETWEEN NOW() - INTERVAL 7 DAY AND NOW()
)t1
left join
(
SELECT DATE_FORMAT(purchase_date, '%Y-%m-%d') as purchase_date,
coalesce(SUM(amount), 0) AS amount
FROM transactions
WHERE purchase_date BETWEEN NOW() - INTERVAL 7 DAY AND NOW()
AND vendor_id = 0
GROUP BY purchase_date
)t2
on t2.purchase_date = t1.purchase_date
group by t1.purchase_date
order by t1.purchase_date desc
DEMO
Simply put together a subquery with the dates you want and use left outer join:
select d.thedate, coalesce(SUM(amount), 0) AS amount
from (select date('2014-04-25') as thedate union all
select date('2014-04-24') union all
select date('2014-04-23') union all
select date('2014-04-22') union all
select date('2014-04-21') union all
select date('2014-04-20') union all
select date('2014-04-19')
) d left outer join
transactions t
on t.purchase_date = d.thedate and vendor_id = 0
GROUP BY d.thedate
ORDER BY d.thedate DESC;
This is for last 7 days;
select d.thedate, coalesce(SUM(amount), 0) AS amount
from (select DATE(NOW()) as thedate union all
select DATE(DATE_SUB( NOW(), INTERVAL 1 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 2 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 3 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 4 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 5 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 6 DAY))) d left outer join
transactions t
on t.purchase_date = d.thedate and vendor_id = 0
GROUP BY d.thedate
ORDER BY d.thedate DESC;
with recursive all_dates(dt) as (
select '2014-04-19' as dt
union all
select dt + interval 1 day
from all_dates
where dt + interval 1 day <= '2014-04-25'
)
select d.dt as purchase_date, coalesce(m.amount, 0) as purchased
from all_dates as d
left join mytable m
on d.dt = m.purchase_date
order by purchase_date desc;