MySQL: Get average of datetime - mysql

I have a table having fields id, cust_id, ord_id, ord_date(datetime datatype). In table, I have data like this..
id cust_id ord_id ord_date prod_id
1 1 1 2015-12-01 00:00:00 1
2 1 1 2015-12-01 00:00:00 2
3 1 1 2015-12-01 00:00:00 3
4 2 1 2015-12-01 00:00:00 1
5 1 3 2015-12-03 00:00:00 1
6 1 3 2015-12-03 00:00:00 2
7 1 2 2015-12-02 00:00:00 1
8 3 1 2015-12-03 00:00:00 1
9 3 1 2015-12-03 00:00:00 2
10 2 2 2015-12-07 00:00:00 1
12 2 2 2015-12-07 00:00:00 2
13 3 2 2015-12-10 00:00:00 1
14 1 4 2015-12-12 00:00:00 1
15 3 3 2015-12-15 00:00:00 1
I have to get data of average order time of each customers with last ord_id (max ord_id) and should be order by cust_id ASC, ord_id DESC.
I want output like this using MYSQL query.
cust_id ord_id ord_date ord_avg_day
1 4 2015-12-12 00:00:00 3
2 2 2015-12-07 00:00:00 3
3 3 2015-12-15 00:00:00 4
I have tried this, but failed as it shows average time 0.
SELECT cust_id, ord_id, ord_date, AVG(TIME_TO_SEC(ord_date)) AS ord_avg_day FROM tableName GROUP BY cust_id, ord_id ORDER BY cust_id, ord_id DESC
I know that it will be easy using normalization. But I have no option for it. I have to work with only this table.
If anyone knows the solution, then answer will be appreciate.

here a simple way to do it. There are some output that you not using. You can delete it. Its only to test.
SELECT
cust_id
, count(*) AS ord_id
, min(ord_date) AS first_order
, max(ord_date) AS last_order
, (DATEDIFF(max(ord_date) , min(ord_date)) ) / (count(*)-1) AS ord_avg_day
FROM (
SELECT *
FROM myorder
GROUP BY ord_date,cust_id
) AS tmp
GROUP BY cust_id;
Result
+---------+--------+---------------------+---------------------+-------------+
| cust_id | ord_id | first_order | last_order | ord_avg_day |
+---------+--------+---------------------+---------------------+-------------+
| 1 | 4 | 2015-12-01 00:00:00 | 2015-12-12 00:00:00 | 3.6667 |
| 2 | 2 | 2015-12-01 00:00:00 | 2015-12-07 00:00:00 | 6.0000 |
| 3 | 3 | 2015-12-03 00:00:00 | 2015-12-15 00:00:00 | 6.0000 |
+---------+--------+---------------------+---------------------+-------------+
3 rows in set (0.00 sec)
I have correct a error.
Please let me know if it works for you

Related

MySql query to find start and end of sequence blocks associated to specific user (not looking for gaps)

How would I go about to write a query that will use this data
ID Timestamp UID
1 2018-01-01 00:00:00 1
2 2018-01-01 00:00:00 1
3 2018-01-01 00:00:01 1
4 2018-01-01 00:00:01 2
5 2018-01-01 00:00:02 2
6 2018-01-01 00:01:00 2
7 2018-01-01 00:01:02 3
8 2018-01-01 00:02:00 3
9 2018-01-01 00:05:00 1
10 2018-01-01 00:05:01 1
11 2018-01-01 00:05:02 1
and return this:
ID1 ID2 Timestamp1 Timestamp2 UID
1 3 2018-01-01 00:00:00 2018-01-01 00:00:01 1
4 6 2018-01-01 00:00:01 2018-01-01 00:01:00 2
7 8 2018-01-01 00:01:02 2018-01-01 00:02:00 3
9 11 2018-01-01 00:05:00 2018-01-01 00:05:02 1
Meaning, i need to get first and last timestamp and first and last ID of each sequence block belonging to same UID (ordered by starting ID, as there is no overlapping)
There will be no ID gaps in blocks as this is a table with permanent undeletable data
Thanks
Assigning a block number is a way to go. The below query does so using a variable in the sub query.
select min(id) minid, max(id) maxid ,blocknumber, min(timestamp) mints, max(timestamp) maxts ,uid
from
(
select t.id,t.Timestamp,t.UID,
if( t.uid <> #p,#bn:=#bn+1,#bn:=#bn) blocknumber,
#p:=t.uid p
from t
cross join(select #bn:=0,#p:=0) r
order by t.id,t.uid
) a
group by uid,blocknumber
order by min(timestamp)
;
+-------+-------+-------------+---------------------+---------------------+------+
| minid | maxid | blocknumber | mints | maxts | uid |
+-------+-------+-------------+---------------------+---------------------+------+
| 1 | 3 | 1 | 2018-01-01 00:00:00 | 2018-01-01 00:00:01 | 1 |
| 4 | 6 | 2 | 2018-01-01 00:00:01 | 2018-01-01 00:01:00 | 2 |
| 7 | 8 | 3 | 2018-01-01 00:01:02 | 2018-01-01 00:02:00 | 3 |
| 9 | 11 | 4 | 2018-01-01 00:05:00 | 2018-01-01 00:05:02 | 1 |
+-------+-------+-------------+---------------------+---------------------+------+
4 rows in set (0.08 sec)
Try this query:
SELECT a.*, b.* FROM
(SELECT MAX(timestamp) as amax, id, uid FROM `test` GROUP by uid) as a,
(SELECT MIN(timestamp) as amin, id, uid FROM `test` GROUP by uid) as b
Please remove test by your table name.

get first record from each day then only check condition

I have user attendance CLOCK IN data like this.
id | userID | created_at
1 | 1 | 2018-06-27 00:15:00
2 | 1 | 2018-06-27 01:43:55
3 | 1 | 2018-06-27 02:43:55
4 | 2 | 2018-06-27 00:15:00
5 | 2 | 2018-06-27 02:43:55
6 | 2 | 2018-06-27 03:43:55
7 | 1 | 2018-06-28 00:55:00
8 | 1 | 2018-06-28 01:43:55
9 | 1 | 2018-06-28 02:43:55
10 | 2 | 2018-06-28 00:00:00
11 | 2 | 2018-06-28 02:43:55
12 | 2 | 2018-06-28 03:43:55
I want a list of dates where user was late to clock in.
Assume company work time is 00:00:00 and
How can I get results like this :
id | userID | created_at
1 | 1 | 2018-06-27 00:15:00
4 | 2 | 2018-06-27 00:15:00
7 | 1 | 2018-06-28 00:55:00
Appreciate any help from you guys.Thanks 🙏🏻
You could try conditionally aggregating by user and date, and then checking to see whether an exact midnight clock in occurred (or did not occur):
SELECT
userID,
MIN(created_at) AS created_at
FROM yourTable
GROUP BY
userID,
DATE(created_at)
HAVING
SUM(CASE WHEN DATE_FORMAT(created_at, '%H:%i:%s') = '00:00:00' THEN 1 ELSE 0 END) = 0;
Demo

Fill in missing dates for multiple values with a GROUP BY

I'm trying to write a single query for retrieving time information for multiple users over a date range.
My tables are set up as follows:
Entry:
user_id | entry_date | entry_hours
1 | 2013-11-12 | 4
1 | 2013-11-12 | 5
1 | 2013-11-13 | 3
2 | 2013-11-13 | 7
....
Calendar:
calendar_date
.....
2013-11-10
2013-11-11
2013-11-12
2013-11-13
2013-11-14
.....
If I run this query:
SELECT *, e.user_id, e.entry_date, COALESCE(SUM(e.entry_hours), 0) as total
FROM `entry` e
WHERE entry_date BETWEEN '2013-11-12' and '2013-11-13'
GROUP BY e.user_id, e.entry_date
I get something to the effect of
user_id | entry_date | total
1 | 2013-11-12 | 9
1 | 2013-11-13 | 3
2 | 2013-11-13 | 7
When I would like this instead:
user_id | entry_date | total
1 | 2013-11-12 | 9
1 | 2013-11-13 | 3
2 | 2013-11-12 | 0
2 | 2013-11-13 | 7
Where it fills in the missing date with a 0.
EDIT: I would also need it to pull in user_id's with 0's when they have no listed entries. Say I included in there WHERE clause AND user_id IN (1,2,3), I would get something like:
user_id | entry_date | total
1 | 2013-11-12 | 9
1 | 2013-11-13 | 3
2 | 2013-11-12 | 0
2 | 2013-11-13 | 7
3 | 2013-11-12 | 0
3 | 2013-11-13 | 0
The idea behind the calendar table was to be able to pull all dates in a range and join them with the entry table to fill in those missing dates, but I've been unable to figure out how to do this.
Can anyone point me in the right direction?
SELECT
u.user_id,
c.calendar_date,
COALESCE(SUM(e.entry_hours), 0)
FROM calendar c
CROSS JOIN users u
LEFT JOIN entry e
ON (c.calendar_date = e.entry_date AND u.user_id = e.user_id)
GROUP BY u.user_id,c.calendar_date

Do a SELECT in MYSQL to return the remainder of an ID COUNT/8

I would like to know how to do a Query in MySQL to get the remainder of the ID_COUNT / 8.
Here's an example table:
ID (INT) | PRODUCT ID | TIME
1 | 14 | 09:34:00
5 | 26 | 09:47:00
5 | 01 | 09:58:00
1 | 02 | 10:10:00
2 | 63 | 10:36:00
4 | 59 | 10:47:00
3 | 18 | 11:00:00
1 | 27 | 11:15:00
5 | 36 | 11:38:00
1 | 44 | 09:34:00
5 | 55 | 09:47:00
5 | 14 | 09:58:00
1 | 24 | 10:10:00
2 | 54 | 10:36:00
4 | 44 | 10:47:00
3 | 54 | 11:00:00
1 | 64 | 11:15:00
5 | 14 | 11:38:00
What i would like to do here is, every time a new row is inserted, I want to get the COUNT of that ID (for example for the ID=5 it would be 6), and them divide that number by 8 (6/8) and then get the remainder of that division.
I'm using PHP by the way, if there's no way of doing it only with MySQL.
Thanks in advance, any question fell free to ask!
Miguel.
Use MOD function
Query for you example
SELECT COUNT(1) MOD 8 AS result
FROM `table`
WHERE id=5
Query for common example
SELECT id, COUNT(1) MOD 8 AS result
FROM `table`
GROUP BY id
SELECT MOD(cnt, 8) FROM (SELECT COUNT(ID) cnt FROM mytable GROUP BY ID)
You can use the mod() function:
select mod(count(*), 8)
from t
where id = #Last_Id
The id is not auto incremented, so you cannot use last_inserted_id().

MySQL How to sum rows over a range of timestamps?

Given a table with a timestamp column, e.g.:
timestamp | id | value
--------------------------------------
2001-01-01 00:00:00 | 1 | 3
2001-01-01 00:00:00 | 2 | 5
--------------------------------------
2001-01-02 00:00:00 | 1 | 6
2001-01-02 00:00:00 | 2 | 10
2001-01-02 00:00:00 | 3 | 7
--------------------------------------
2001-01-03 00:00:00 | 3 | 14
2001-01-03 00:00:00 | 2 | 15
--------------------------------------
2001-01-03 00:00:00 | 1 | 9
2001-01-03 00:00:00 | 2 | 20
and a given aggregation level, say 2 day, I would like to aggregate (sum) the results over:
(1) a moving window of the given agg-level, for the above example: 2001-01-01 to 2001-01-02, 2001-01-02 to 2001-01-03, 2001-01-03 to 2001-01-04 which will result in:
timestamp_1 | timestamp_2 | id | agg_value
-----------------------------------------------------------
2001-01-01 00:00:00 | 2001-01-02 00:00:00 | 1 | 9 (=3+6)
2001-01-01 00:00:00 | 2001-01-02 00:00:00 | 2 | 15 (=5+10)
2001-01-01 00:00:00 | 2001-01-02 00:00:00 | 3 | 7 (=7)
-----------------------------------------------------------
2001-01-02 00:00:00 | 2001-01-03 00:00:00 | 1 | 6 (=6)
2001-01-02 00:00:00 | 2001-01-03 00:00:00 | 2 | 25 (=10+15)
2001-01-02 00:00:00 | 2001-01-03 00:00:00 | 3 | 21 (=7+14)
-----------------------------------------------------------
2001-01-03 00:00:00 | 2001-01-04 00:00:00 | 1 | 9 (=9)
2001-01-03 00:00:00 | 2001-01-04 00:00:00 | 2 | 35 (=15+20)
2001-01-03 00:00:00 | 2001-01-04 00:00:00 | 3 | 14 (=14)
(2) non overlapping division to the given range, for the above example: 2001-01-01 to 2001-01-02, 2001-01-03 to 2001-01-04, which will result in:
timestamp_1 | timestamp_2 | id | agg_value
-----------------------------------------------------------
2001-01-01 00:00:00 | 2001-01-02 00:00:00 | 1 | 9 (=3+6)
2001-01-01 00:00:00 | 2001-01-02 00:00:00 | 2 | 15 (=5+10)
2001-01-01 00:00:00 | 2001-01-02 00:00:00 | 3 | 7 (=7)
-----------------------------------------------------------
2001-01-03 00:00:00 | 2001-01-04 00:00:00 | 1 | 9 (=9)
2001-01-03 00:00:00 | 2001-01-04 00:00:00 | 2 | 35 (=15+20)
2001-01-03 00:00:00 | 2001-01-04 00:00:00 | 3 | 14 (=14)
(which is basically like (1) without the overlap)
Thanks!
Edited: adding a solution
I have a solution at least for (1):
SELECT t1.timestamp AS timestamp1,
MAX(t2.timestamp) AS timestamp2, t1.id,
SUM(t2.value) AS agg_value
FROM my_table t1
LEFT JOIN my_table t2 ON
(t2.timestamp >= t1.timestamp AND
t2.timestamp <= ADDDATE(t1.timestamp,INTERVAL 2 DAY) AND
t2.id = t1.id)
GROUP BY t1.timestamp, t1.id
A solution for (2) can probably be just filtering to a subset of the above.
This will group by every X days by getting the date portion and getting the date difference and grouping by the days difference, PLUS the ID. This will get your #2 solution
select
CEILING( datediff( date( now() ), date( myTimeStamp )) / 2 ) DaysDiff,
ID,
min( date( myTimeStamp )) as FirstDateInGroup,
max( date( myTimeStamp )) as LastDateInGroup,
sum( value ) as SumVal
FROM TimeSample
group by DaysDiff, ID
order by FirstDateInGroup, ID
EDIT ---- PER COMMENTS
Your sample was showing how to handle for 2 days... so does this. The "now()" is just a baseline to group your data. If you want it broken down by years, then I would just do a query based on the YEAR( YourDateColumn ) as the group. If you want 30 days, just divide by 30. Monthly, I would group by year( YourDateColumn ) and month( YourDateColumn ) respectively. By having a fixed "now()" range, its doing nothing but returning a number as a starting point. If your data was 2 years old, the date difference would just be 365days * 2 years = 730 days... Divide by 2 and your back at a group basis of 365. You can throw whatever where clause you want to further limit the time period you are interested in...
where myTimeStamp between '2011-01-01' and '2011-06-30' to get just the first 6 months of this year... That would result with your DaysDiff grouping of 208 days / 2 = 104.
So, if you have some other baseline value you care to make your groupings, you can just change the now() to something like '2011-01-01' and it will compute based on Jan 1, 2011 basis. Of which, this will do nothing but compute the DaysDiff to negative values up to zero, then back to positive.