TIMEDIFF GROUP BY involved days - php

I am searching for a way to get the TIMEDIFF of two timestamps grouped by involved days in MySQL.
SELECT TIMEDIFF('2012-01-02 10:00:00', '2012-01-01 10:00:00');
This is just giving the diff of 24 hours.
I need the diff of both (or maybe more) days. i.e.:
2012-01-01: 14:00:00
2012-01-02: 10:00:00
[...]
Is there a way to group in the TIMEDIFF function?

In order to do what you want, you need to generate a sequence of numbers. Perhaps you have a table. If not, something like the following:
select dstart + interval n.num days
from (select d1*10+d2 as num
from (select 0 as d 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
) d1 cross join
(select 0 as d 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
) d2
) n join
(select date('2012-01-02') as dend, date('2012-01-01') dstart
on dstart + interval n.num days <= dend
(This is not tested, so it might have syntax errors.)

Related

Make zero instead of null if there are no records in for mysql SUM query with group by [duplicate]

This question already has answers here:
MySQL how to fill missing dates in range?
(6 answers)
Closed 3 years ago.
I writes a query to get the result like sum of the amount for each product group by date.
When getting the output some products does not have corresponding amount for the date.that dates are not listing in the output So i need every date to display and if amount is there it need to show the amount otherwise 0.
I tried using IFNULL and COALESCE but getting same output.
My OUTPUT
date amount
2019-05-16 499
2019-05-17 1998
2019-05-18 999
2019-05-19 999
Needed output should look like this
date amount
2019-05-16 499
2019-05-17 1998
2019-05-18 999
2019-05-19 999
2019-05-20 0
query for getting data from mysql
$query = "select date, SUM(amount) as amount from product_details where date between '2019-05-16 00:00:00' and '2019-05-20 23:59:59' and prodid = 1 group by date";
Is there any way to make appear the date which does not have amount?
Try this. First we generate a record set of all dates between curdate() and first jan. Change curdate() to end date and 1 jan to start date.
Then we outer join this with your table and use it to group by date.
With cal as (SELECT a.Days
FROM (
SELECT curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY AS Days
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.Days >= '2019-01-01' and a.Days<=curdate())
SELECT cal.Days as date, sum(p.amount) as amount FROM cal LEFT JOIN product_details p on cal.Days=p.date and p.prodid=1 group by cal.Days

0 When no row exist

I have following sql query
SELECT DATE_FORMAT(date,'%d.%m. %y') as date, COUNT(idStat) as number
FROM stat
WHERE IdGame = ? AND date >= ? AND date <= ?
GROUP BY date
It returns date and how much people visited game this day, but how to return 0, when no rows exist this day?
For example day 15.12.1993 does not exist in db, but user pick date between 15.10.1950 and 12.15.2020.
I want to return this non existing date 15.12.1993 but with count 0.
Is this even possible?
Thanks for help,
Filip.
The best way is to have a Calendar table handy with relevant dates. You can then use a left join to get the dates. Something like this:
SELECT DATE_FORMAT(c.date,'%d.%m. %y') as date, COUNT(s.idStat) as number
FROM Calendar c LEFT JOIN
stat s
ON c.date = s.date AND s.IdGame = ?
WHERE c.date >= ? AND c.date <= ?
GROUP BY c.date;
If you have games on every date but the problem is that the particular game is not one the day, you can use this short-cut:
SELECT DATE_FORMAT(date,'%d.%m. %y') as date, SUM(IdGame = ?) as number
FROM stat
WHERE date >= ? AND date <= ?
GROUP BY date;
This doesn't work in all cases, but it can be a useful short-cut.
I used RedFilter Answer to solve my problem, from this link: generate days from date range
My query now looks like this:
select DATE_FORMAT(a.Date,'%d.%m. %Y'), COUNT(stat.IdStat)
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
) as a LEFT JOIN stat ON a.Date = stat.date
where a.Date between ? and ? AND (stat.IdGame = ? OR stat.IdGame IS NULL) GROUP BY a.Date
But I need remove future dates form my datapicker, because when i use futue date in this sql, no data will be return... I need set min and max of my datapicker.

How to get separate sums of last seven days from SQL with 0 value included

In my CRM system I have table with leads. I would like to make a chart to see how many leads were added in last 7 days. For that purpose I need to have separete sums for every day from last week.
My table called tab_leads comes with lead_id (integer) and lead_create_date (time stamp, format: 0000-00-00 00:00:00)
So I need something like:
Day 1 - 10
Day 2 - 0
Day 3 - 5
Day 4 - 0
Day 5 - 9
Day 6 - 15
Day 7 (today) - 0
At the moment I am usign this query:
SELECT
DATE(lead_create_date) AS `Date`,
COUNT(*) AS `Leads`
FROM
tab_leads
WHERE
lead_create_date >= CURRENT_DATE - INTERVAL 6 DAY
GROUP BY
DATE(lead_create_date)
But the problem is, that if in any of those days we do not hava any data (ex. weekend) I am getting less than 7 sums. Ex:
Day 1 - 10
Day 2 - 5
Day 3 - 9
Day 4 - 15
For drawing a chart I need to have always seven sums, even with 0 value.
How to do that in MySQL or MySQL + PHP?
..UPDATE:
I am just trying to create SQL Fiddle withous success.
Sample data:
CREATE TABLE tab_leads (
`lead_id` int,
`lead_create_date` timestamp
) ENGINE=InnoDB
INSERT INTO tab_leads
(`lead_id`, `lead_create_date`)
VALUES
(0, '2015-05-02 05:30:40'),
(1, '2015-05-02 00:00:00'),
(2, '2015-05-03 00:00:00'),
(3, '2015-05-03 00:00:00'),
(4, '2015-05-05 00:00:00'),
(5, '2015-05-06 00:00:00'),
(6, '2015-05-07 00:00:00'),
(7, '2015-05-08 00:00:00'),
(8, '2015-05-08 00:00:00')
;
Here you go the query is
select
t1.Date,
coalesce(t2.Leads, 0) AS Leads
from
(
select DATE_FORMAT(a.Date,'%Y-%m-%d') as Date
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 curdate() - interval 7 DAY AND curdate()
)t1
left join
(
SELECT
DATE(lead_create_date) AS `Date`,
COUNT(*) AS `Leads`
from tab_leads
WHERE
lead_create_date >= CURRENT_DATE - INTERVAL 6 DAY
GROUP BY DATE(lead_create_date)
)t2
on t2.Date = t1.Date
group by t1.Date
order by t1.Date desc
The first part of the query is just to generate the dates for the given range and then use it as a left table and do a left join to your original query.
From the sample data you will get result as
+------------+-------+
| Date | Leads |
+------------+-------+
| 2015-05-08 | 2 |
| 2015-05-07 | 1 |
| 2015-05-06 | 1 |
| 2015-05-05 | 1 |
| 2015-05-04 | 0 |
| 2015-05-03 | 2 |
| 2015-05-02 | 2 |
| 2015-05-01 | 0 |
+------------+-------+
8 rows in set (0.02 sec)
Not pretty but try this?
SELECT tmp.dt, src.Leads
FROM (
SELECT CURRENT_DATE - INTERVAL 6 DAY as dt UNION
SELECT CURRENT_DATE - INTERVAL 5 DAY UNION
SELECT CURRENT_DATE - INTERVAL 4 DAY UNION
SELECT CURRENT_DATE - INTERVAL 3 DAY UNION
SELECT CURRENT_DATE - INTERVAL 2 DAY UNION
SELECT CURRENT_DATE - INTERVAL 1 DAY UNION
SELECT CURRENT_DATE) as tmp
left join (
SELECT
DATE(lead_create_date ) AS `Date`,
COUNT(*) AS `Leads`
FROM
tab_leads
WHERE
lead_create_date >= CURRENT_DATE - INTERVAL 6 DAY
GROUP BY
DATE(lead_create_date )
) as src on tmp.dt = src.`Date`
Because in your original table there is no data for the given dates, you can't just return a row with 0. You may see common examples such as:
Give the count of each [some field] for each user, even if it is 0.
These examples can be done, assuming there is a record for each user somewhere, even if there isn't a record for some field. In your case, you don't have a record for days 2, 4, or 7, so they can't be returned.
To work around this, you need to create a temporary table of the last 7 days. Using an example given here:
select a.Date
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 DATE_SUB(NOW(), INTERVAL 7 DAY) and NOW();
This will give you the temporary table you need. Then, you can preform your aggregation like this:
SELECT lead_create_date AS date, COUNT(*) AS numLeads
FROM myTable
GROUP BY lead_create_date;
And then outer join that to your temporary table which will fill in 0 values:
SELECT tmp.date, COALESCE(m.numLeads, 0) AS numLeads
FROM(
SELECT a.Date
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 DATE_SUB(NOW(), INTERVAL 7 DAY) AND NOW()) tmp
LEFT JOIN(
SELECT lead_create_date AS date, COUNT(*) AS numLeads
FROM myTable
GROUP BY lead_create_date) m ON m.date = tmp.date
ORDER BY tmp.date;
It looks monstrous, but it works. Based on the comments in the other answer, it looks to work pretty well too.
Here is an SQL Fiddle example.
EDIT
Some clarification, the first query I gave was just to show how to create a temporary table with the last 7 dates. The second query shows how to preform aggregation on the whole table to get the counts for all dates in your table. The third query combines them together to only show the counts of the last seven days, which is the answer you are looking for.

MySQL inner join three tables to use in calendar

Following up my question where I used the answer to generate data on my calendar called maintenance calendar showing the aircraft's maintenance schedule. This is the MySQL query for it:
SELECT DISTINCT s.reg AS 'reg',
a.date AS 'date'
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
INNER JOIN maintenance_sched s
ON a.date >= s.date_from
AND a.date <= s.date_to
WHERE Month(date) = '".$month."'
AND Dayofmonth(date) = '".$dayArray["mday"]."'
AND Year(date) = '".$year."'
Here is the maintenance_sched database:
And the calendar looks like this (based on the data from maintenance_sched):
Then, I have another calendar called reservation calendar with the same code as the maintenance calendar though with different query. This is the reservation calendar query: SELECT acode FROM reservation WHERE month(etd) = '".$month."' AND dayofmonth(etd) = '".$dayArray["mday"]."' AND year(etd) = '".$year."' ORDER BY etd".
The reservation table is this:
And the reservation calendar looks like this:
EDIT:
What I want to do is: have these two calendar in one calendar with the result of maintenance_sched query outputted as string with strikethrough. But I can't seem to make the two queries work out together as one.
I do think the answer to this question is to simply join the two queries. An example of this might be like below where you just null out any columns that aren't in your second table.
SELECT id, date, field3, description
FROM table1
UNION
SELECT id, date, field3, null
FROM table2
As there is no relationship among both the table we cannot go for joins, it would be better to go for UNION to combine the result.
This query uses group_concat so will generate common results in following form
2013-03-15 | RP-C1728, RP-C1086
2013-03-08 | RP-C1728, RP-C1086, RP-C143
If you dont want record in this format then just remove group_concat, group by clause from the query.
Query
SELECT a.date, group_concat(a.reg)
FROM
(SELECT DISTINCT s.reg AS 'reg',
a.date AS 'date'
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
INNER JOIN maintenance_sched s
ON a.date >= s.date_from
AND a.date <= s.date_to
WHERE Month(date) = '".$month."'
AND Dayofmonth(date) = '".$dayArray["mday"]."'
AND Year(date) = '".$year."'
UNION ALL
SELECT acode as 'reg', date as 'date' //Add the date logic here as per your need
FROM reservation
WHERE month(etd) = '".$month."' AND
dayofmonth(etd) = '".$dayArray["mday"]."' AND
year(etd) = '".$year."' ORDER BY etd) a
GROUP BY a.date;
NOTE For the second query add the according date logic

How to echo days with no results in while loop?

I want to count the number of posts for each day to create a graph. My problem is that since SQL doesn't find results for some days (Count is 0), I'm missing rows I need for the chart (since I do want to show days with no posts).
SELECT DATE(Date) AS Day, COUNT(*) AS COUNT
FROM `Posts`
GROUP By `Day`
ORDER BY Date DESC
while($row = mysql_fetch_array($result)) {
echo $row['Date'] . ": " . $row['Count'];
}
Since the loop doesn't display days with 0 results, if on wednesday there are no posts I get: monday-17-3: 5, tuesday-18-3: 2, thursday-20-3: 3. Instead I want to fill out the blanks so I get something like: wednesday-19-3: 0.
How can I echo the days with no results in the loop?
You can work around this by a table of dates, performing an OUTER JOIN, and then performing the grouping. This will provide you with the dates in between (Disclaimer: I'm assuming your dates are in the format YYYY-MM-DD, otherwise you may need to tweak the JOIN statement slightly.).
SELECT A.Date AS Day, COUNT(Posts.Date) AS COUNT
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
LEFT OUTER JOIN `Posts` ON A.Date = `Posts`.`Date`
WHERE A.Date >= DATE_ADD(CURDATE(), INTERVAL -15 DAY)
GROUP BY A.Date
For the date table, I'm using the method from the following post: generate days from date range
Use a loop to go through successive dates, using a function like:
$date = strtotime(date("Y-m-d", strtotime($date)) . " +1 day");
For each cycle, apply your query result. Then you'll have all the dates.

Categories