MySQL String to DATE / TIME or TIMESTAMP - mysql

In my data set, the start and end time for a task is given as a string. The string contains:
'Day, Date Month YYYY HH:MM:SS GMT'
'Wed, 18 Oct 2017 10:11:03 GMT'
The previous questions on Stack Overflow do not have data in this format and I have been struggling how to convert it into DATE/TIME or TIMESTAMP. Any advice on this would be greatly appreciated!
This post was quite relevant but still does not meet my needs, as the format of the string is different in both cases:
Converting date/time string to unix timestamp in MySQL
Overall, I want to achieve a variable 'time_on_task' which takes the difference per person between their start_time and end_time. Thus, for the following data:
Person TaskID Start_time End_time
Alpha 1 'Wed, 18 Oct 2017 10:10:03 GMT' 'Wed. 18 Oct 2017 10:10:36 GMT'
Alpha 2 'Wed, 18 Oct 2017 10:11:16 GMT' 'Wed, 18 Oct 2017 10:11:28 GMT'
Beta 1 'Wed, 18 Oct 2017 10:12:03 GMT' 'Wed, 18 Oct 2017 10:12:49 GMT'
Alpha 3 'Wed, 18 Oct 2017 10:12:03 GMT' 'Wed, 18 Oct 2017 10:13:13 GMT'
Gamma 1 'Fri, 27 Oct 2017 22:57:12 GMT' 'Sat, 28 Oct 2017 02:00:54 GMT'
Beta 2 'Wed, 18 Oct 2017 10:13:40 GMT' 'Wed, 18 Oct 2017 10:14:03 GMT'
The required output would be something like this:
Person TaskID Time_on_task
Alpha 1 0:00:33 #['Wed, 18 Oct 2017 10:10:36 GMT' - 'Wed, 18 Oct 2017 10:10:03 GMT']
Alpha 2 0:00:12 #['Wed, 18 Oct 2017 10:11:28 GMT' - 'Wed, 18 Oct 2017 10:11:16 GMT']
Beta 1 0:00:46 #['Wed, 18 Oct 2017 10:12:49 GMT' - 'Wed, 18 Oct 2017 10:12:03 GMT']
Alpha 3 0:01:10 #['Sat, 18 Nov 2017 10:13:13 GMT' - 'Sat, 18 Nov 2017 10:12:03 GMT']
Gamma 1 3:03:42 #['Sat, 28 Oct 2017 02:00:54 GMT' - 'Fri, 27 Oct 2017 22:57:12 GMT']
Beta 2 0:00:23 #['Wed, 18 Oct 2017 10:14:03 GMT' - 'Wed, 18 Oct 2017 10:13:40 GMT']

You need STR_TO_DATE() to convert the string to a date. Consider:
select str_to_date(
'Wed, 18 Oct 2017 10:11:03 GMT',
'%a, %d %b %Y %T GMT'
)
Yields:
2017-10-18 10:11:03
Once you strings are converted to dates, you can use timestampdiff() to compute the difference between them, and turn the result back to a time using sec_to_time():
select
person,
taskID,
sec_to_time(
timestampdiff(
second,
str_to_date(Start_time, '%a, %d %b %Y %T GMT'),
str_to_date(End_time, '%a, %d %b %Y %T GMT')
)
) time_on_task
from mytable
Demo on DB Fiddlde:
| person | taskID | time_on_task |
| ------ | ------ | ------------ |
| Alpha | 1 | 00:00:33 |
| Alpha | 2 | 00:00:12 |
| Beta | 1 | 00:00:46 |
| Alpha | 3 | 00:01:10 |
| Gamma | 1 | 03:03:42 |
| Beta | 2 | 00:00:23 |

Related

Add rows for each day that lies in a period in MYSQL

I am working on a table 'booking' -
I want to add a column 'date_of_stay' to this table where date_of_stay will store each date in the period that a booking_id will stay for as per the number of nights given by column 'nights'.
FOR eg-
booking_id booking_date nights date_of_stay
5001 Thu, 03 Nov 2016 7 Thu, 03 Nov 2016
5001 Thu, 03 Nov 2016 7 Fri, 04 Nov 2016
5001 Thu, 03 Nov 2016 7 Sat, 05 Nov 2016
5001 Thu, 03 Nov 2016 7 Sun, 06 Nov 2016
5001 Thu, 03 Nov 2016 7 Mon, 07 Nov 2016
5001 Thu, 03 Nov 2016 7 Tue, 08 Nov 2016
5001 Thu, 03 Nov 2016 7 Wed, 09 Nov 2016
5002 Thu, 03 Nov 2016 2 Thu, 03 Nov 2016
5002 Thu, 03 Nov 2016 2 Fri, 04 Nov 2016
What can be the simplest way of viewing my table like this without altering it?
Use a recursive CTE:
with cte as (
select booking_id, booking_date, nights, 1 as n
from t
union all
select booking_id, booking_date, nights, 1 + n
from cte
where n < nights
)
select *
from cte;
According this article you can use next query:
select booking.*, date_of_stay
from (
select adddate('2015-01-01', t3*1000 + t2*100 + t1*10 + t0) date_of_stay from
(select 0 t0 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 t1 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 t2 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 t3 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
) v
join booking ON date_of_stay between
booking.booking_date and date_add(booking.booking_date, interval nights-1 day)
order by booking_id, date_of_stay
;
The above query works in MYSQL 5 & 8 as well. It is valid for all dates in interval:
select '2015-01-01' from_date, adddate('2015-01-01',9*1000 + 9*100 + 9*10 + 9) to_date;
+------------+------------+
| from_date | to_date |
+------------+------------+
| 2015-01-01 | 2042-05-18 |
+------------+------------+
Here query can be tested SQLize.online
You can do it with a recursive CTE:
WITH RECURSIVE cte AS (
SELECT booking_id, booking_date, nights, 0 nr,
STR_TO_DATE(booking_date, '%a, %d %b %Y') date_of_stay
FROM booking
UNION ALL
SELECT booking_id, booking_date, nights, nr + 1,
date_of_stay + interval 1 day
FROM cte
WHERE nr < nights - 1
)
SELECT c.booking_id, c.booking_date, c.nights,
DATE_FORMAT(c.date_of_stay, '%a, %d %b %Y') date_of_stay
FROM cte c
ORDER BY c.booking_id, c.date_of_stay
See the demo.
Results:
> booking_id | booking_date | nights | date_of_stay
> ---------: | :--------------- | -----: | :---------------
> 5001 | Thu, 03 Nov 2016 | 7 | Thu, 03 Nov 2016
> 5001 | Thu, 03 Nov 2016 | 7 | Fri, 04 Nov 2016
> 5001 | Thu, 03 Nov 2016 | 7 | Sat, 05 Nov 2016
> 5001 | Thu, 03 Nov 2016 | 7 | Sun, 06 Nov 2016
> 5001 | Thu, 03 Nov 2016 | 7 | Mon, 07 Nov 2016
> 5001 | Thu, 03 Nov 2016 | 7 | Tue, 08 Nov 2016
> 5001 | Thu, 03 Nov 2016 | 7 | Wed, 09 Nov 2016
> 5002 | Thu, 03 Nov 2016 | 2 | Thu, 03 Nov 2016
> 5002 | Thu, 03 Nov 2016 | 2 | Fri, 04 Nov 2016

Rolling sum for a column in mysql

I would like to get the rolling sum for the growth column for the following sample data which can be found here dbfiddle
The output should look like,
Growth RollingSum TMonth TYear
511 511 AUG 2019
79 590 SEP 2019
-6 584 OCT 2019
0 584 NOV 2019
-4 580 DEC 2019
45 625 JAN 2020
-1 624 FEB 2020
7 631 MAR 2020
-22 609 APR 2020
-6 603 MAY 2020
-20 583 JUN 2020
0 583 JUL 2020
My attempt is as follows. I am only getting the total for values in the Growth column
SELECT r1.Growth, sum(r2.Growth) AS rolling_total ,r1.Month,r1.Year
FROM Report AS r1 JOIN Report AS r2
ON r1.Month = r1.Month
GROUP BY r1.Month;
The above gives me the result as,
Growth RollingSum TMonth TYear
511 583 AUG 2019
79 583 SEP 2019
-6 583 OCT 2019
0 583 NOV 2019
-4 583 DEC 2019
45 583 JAN 2020
-1 583 FEB 2020
7 583 MAR 2020
-22 583 APR 2020
-6 583 MAY 2020
-20 583 JUN 2020
0 583 JUL 2020
I want to avoid using procedures,cursors or functions if at all possible. I am using mysql 8.0.17
You can use SUM() window function but with the correct ordering.
This ordering:
ORDER BY DATE_FORMAT(`TMonth`, '%M'), `TYear`
is wrong because DATE_FORMAT() with these parameters returns NULL, so you simply sort by Year.
See the demo.
It is coincidental that you get correct results.
The correct ordering is:
ORDER BY STR_TO_DATE(CONCAT(TYear, TMonth, '01'), '%Y%b%d')
So use this:
SELECT
Growth,
SUM(Growth) OVER (ORDER BY STR_TO_DATE(CONCAT(TYear, TMonth, '01'), '%Y%b%d')) RollingSum,
TMonth,
TYear
FROM Report
See the demo.
Results:
> Growth | RollingSum | TMonth | TYear
> -----: | ---------: | :----- | ----:
> 511 | 511 | AUG | 2019
> 79 | 590 | SEP | 2019
> -6 | 584 | OCT | 2019
> 0 | 584 | NOV | 2019
> -4 | 580 | DEC | 2019
> 45 | 625 | JAN | 2020
> -1 | 624 | FEB | 2020
> 7 | 631 | MAR | 2020
> -22 | 609 | APR | 2020
> -6 | 603 | MAY | 2020
> -20 | 583 | JUN | 2020
> 0 | 583 | JUL | 2020
Since you are using mysql 8 use window functionn SUM
DATE_FOMAT are not working, so you need STR_TO_DATE
CREATE TABLE roll_month (
`Growth` INTEGER,
`RollingSum` INTEGER,
`TMonth` VARCHAR(3),
`TYear` INTEGER
);
INSERT INTO roll_month
(`Growth`, `RollingSum`, `TMonth`, `TYear`)
VALUES
('511', '511', 'AUG', '2019'),
('79', '590', 'SEP', '2019'),
('-6', '584', 'OCT', '2019'),
('0', '584', 'NOV', '2019'),
('-4', '580', 'DEC', '2019'),
('45', '625', 'JAN', '2020'),
('-1', '624', 'FEB', '2020'),
('7', '631', 'MAR', '2020'),
('-22', '609', 'APR', '2020'),
('-6', '603', 'MAY', '2020'),
('-20', '583', 'JUN', '2020'),
('0', '583', 'JUL', '2020');
SELECT
`Growth`
, SUM(`Growth`) OVER( ORDER BY STR_TO_DATE(CONCAT('01-',`TMonth`,'-',`TYear`), '%d-%M-%Y') ROWS UNBOUNDED PRECEDING) RollingSum
, `TMonth`, `TYear`
FROM roll_month
Growth | RollingSum | TMonth | TYear
-----: | ---------: | :----- | ----:
511 | 511 | AUG | 2019
79 | 590 | SEP | 2019
-6 | 584 | OCT | 2019
0 | 584 | NOV | 2019
-4 | 580 | DEC | 2019
45 | 625 | JAN | 2020
-1 | 624 | FEB | 2020
7 | 631 | MAR | 2020
-22 | 609 | APR | 2020
-6 | 603 | MAY | 2020
-20 | 583 | JUN | 2020
0 | 583 | JUL | 2020
db<>fiddle here
With the values and names that i took from your table, this cross join would give you the output you want way more easy:
select r1.Growth, r1.TMonth, TYear,
(#s := #s + r1.Growth) as RollingSum
from Report r1 cross join
(select #s := 0) p;

ORDER BY MONTH(1) after GROUP_CONCAT as names?

If I don't use GROUP_CONCAT() then there is no difficulty to the group and order the rows according to date-month-year
Following code:
SELECT FROM_UNIXTIME(orders.date_time,'%d %m %Y') AS date,
SUM(orders.net_amount) AS total_sales,
COUNT(FROM_UNIXTIME(orders.date_time,'%D %b %Y')) AS total_orders
FROM orders
JOIN users ON orders.user_id = users.id
WHERE FROM_UNIXTIME(orders.date_time,'%d %m %Y') != DATE_FORMAT(users.reg_date_time, '%d %m %Y')
GROUP BY date
ORDER BY Month(1)
O/P:
21 12 2019 1092 1 pinky
04 01 2020 1050 1 harshit
30 12 2019 21 1 robin
05 01 2020 987 2 chetan
31 12 2019 1239 2 rahul
30 11 2019 157.5 1 rahul
01 01 2020 651 1 rahul
15 12 2019 1575 1 isha
03 01 2020 598.5 1 manvi
SEE the names are not concating
But as soon as I add this line:
GROUP_CONCAT(users.firstname SEPARATOR '-')) AS names
like this:
SELECT FROM_UNIXTIME(orders.date_time,'%d %m %Y') AS date,
SUM(orders.net_amount) AS total_sales,
GROUP_CONCAT(users.firstname SEPARATOR '-') AS names,
COUNT(FROM_UNIXTIME(orders.date_time,'%D %b %Y')) AS total_orders
FROM orders
JOIN users ON orders.user_id = users.id
WHERE FROM_UNIXTIME(orders.date_time,'%d %m %Y') != DATE_FORMAT(users.reg_date_time, '%d %m %Y')
GROUP BY date
ORDER BY Month(1)
O/P:
01 01 2020 651 1 rahul
03 01 2020 598.5 1 manvi
04 01 2020 1050 1 harshit
05 01 2020 987 2 chetan-saurabh
15 12 2019 1575 1 isha
21 12 2019 1092 1 pinky
30 11 2019 157.5 1 rahul
30 12 2019 21 1 robin
31 12 2019 1239 2 rahul-manvi
then the order changed by day-order(without proper month and year order) but the grouping is correct.
Am I doing something wrong?
Use ORDER BY MONTH(orders.date_time). The problem is that your date column is not formatted as a valid MySQL date, so it's not extracting the month correctly.

SQL sum and show row with non-existent sum values

i have 2 tables : dt_user and dt_invoice.
**dt_members :**
id firstname
3 Salim
5 Sara
8 Julie
**dt_invoice**
user_id amount_ht period month year
3 4950 04 2018 04 2018
3 7200 10 2018 10 2018
8 11000 10 2018 10 2018
8 5500 11 2018 11 2018
3 6750 11 2018 11 2018
3 8700 12 2018 12 2018
3 8800 01 2019 01 2019
8 7500 01 2019 01 2019
3 4950 02 2019 02 2019
3 7550 03 2019 03 2019
I want to create a query joining the two table, but i want to show each user_id for PERIOD that there is in table dt_invoice.
**Expected results :**
user_id amount_ht period month year
3 4950 04 2018 04 2018
5 0 04 2018 04 2018 //non-existent record in dt_invoice
8 0 04 2018 04 2018 //non-existent record in dt_invoice
3 7200 10 2018 10 2018
5 0 10 2018 10 2018 //non-existent record in dt_invoice
8 11000 10 2018 10 2018
8 5500 11 2018 11 2018
5 0 11 2018 11 2018 //etc ...
3 6750 11 2018 11 2018
3 8700 12 2018 12 2018
5 0 12 2018 12 2018
8 0 12 2018 12 2018
3 8800 01 2019 01 2019
5 0 01 2019 01 2019
8 7500 01 2019 01 2019
3 4950 02 2019 02 2019
5 0 02 2019 02 2018
8 0 02 2019 02 2018
3 7550 03 2019 03 2019
5 0 03 2019 03 2018
8 0 03 2019 03 2018
Thanks in advance for your help, i'm totally stuck ..
SQL datas available here : https://rextester.com/live/LBSEY76360
also in sqlfiddle : http://sqlfiddle.com/#!9/728af3/1
Use a cross join to generate the rows and left join to bring in the values:
select m.user_id, p.period, p.month, p.year,
coalesce(t.amount_ht, 0) as amount_ht
from dt_members m cross join
(select distinct period, month, year from dt_invoice) p left join
dt_invoice t
on t.user_id = m.id and t.period = p.period;
Maybe this would help.
SELECT user_id, amount_ht, period, month, year
FROM dt_invoice
LEFT JOIN dt_members ON user_id = id

mysql list data excluding weekends

I have table with id (PK,AI), name and date, when users register I set date = CURDATE().
I have a PHP page where I have to list the names where CURDATE() >= (date + 3 days) excluding weekends (Sat and Sunday) means I list names of users who completed the registration before 3 days but I don't count weekends.
which means if someone register on Monday, he should listed on the page on Thursday and if he registered on Friday, the page list him only on Wednesday (excluded weekends)
Is there any method in MySQL to accomplish this ?
Instead of doing a complex mysql query, could you not just pass in a different date to check against, depending on the weekday from your php script?
So, instead of comparing in your sql query your date+3days <= today, pass in a variable for the date offset or just calculate the date in php and pass the date in.
day of the week | offset
1 | 5
2 | 5
3 | 5
4 | 3
5 | 3
6 | 4
7 | 5
Easy in MySQL.
SELECT CURRENT_DATE + interval (3 + 2*(weekday(CURRENt_DATE) > 1)) day;
Thanks to #JamesBlond for the table, saves me to explain. ;-)
EDIT: I was confused by the (index-killing) way to request data: normally you should try taking the date from the table unmodified and to fiddle with your CURRENT_DATE data.
As long as I understand you right, it should be the following, so you also could simply use a CASE WHEN clasue, that is more readable and easier to adopt.
thedate - interval (
3 + 2*(weekday(thedate) < 3)
+ (weekday(thedate) > 4) * (weekday(thedate)-4)) DAY
AS 3_days_before
http://www.sqlfiddle.com/#!2/49731/9/0
THEDATE WEEKDAY(THEDATE) THEDATE_FORMATTED 3_DAYS_BEFORE FORMATTED_3_DAYS_BEFORE
April, 27 2014 00:00:00+0000 6 Sunday April, 22 2014 00:00:00+0000 Tuesday
April, 28 2014 00:00:00+0000 0 Monday April, 23 2014 00:00:00+0000 Wednesday
April, 29 2014 00:00:00+0000 1 Tuesday April, 24 2014 00:00:00+0000 Thursday
April, 30 2014 00:00:00+0000 2 Wednesday April, 25 2014 00:00:00+0000 Friday
May, 01 2014 00:00:00+0000 3 Thursday April, 28 2014 00:00:00+0000 Monday
May, 02 2014 00:00:00+0000 4 Friday April, 29 2014 00:00:00+0000 Tuesday
May, 03 2014 00:00:00+0000 5 Saturday April, 29 2014 00:00:00+0000 Tuesday
May, 04 2014 00:00:00+0000 6 Sunday April, 29 2014 00:00:00+0000 Tuesday
May, 05 2014 00:00:00+0000 0 Monday April, 30 2014 00:00:00+0000 Wednesday
May, 06 2014 00:00:00+0000 1 Tuesday May, 01 2014 00:00:00+0000 Thursday
May, 07 2014 00:00:00+0000 2 Wednesday May, 02 2014 00:00:00+0000 Friday