Mysql time spent at work by specyfic user - mysql

I have a MySQL table like this:
+-----+----------+------------+--------------+-------------+
| id | user_id | added_on | status_enter | status_exit |
+-----+----------+------------+--------------+-------------+
Is it possible to count the time if the data is in other rows?
12:16:16 - 10:44:1
User Date Enter Exit
----------- -------------------- ------ ------
John 2021-06-25 10:44:15 1 0
John 2021-06-25 12:16:16 0 1

Not tested, but SHOULD get what you are looking for. The outer query is only looking for those where a person clocked IN. The 3rd column-based select is a correlated query to whatever the current user is and the ID is greater than the check-in, AND it is the check-out. So its possible a null value here if the person is still clocked-in. I would have an index on this table by (enter, user, exit, id) to help optimize the query.
select
tc.id,
tc.user,
tc.date,
( select min( tc2.date )
from TimeClockTable tc2
where tc.User = tc2.User
and tc.id < tc2.id
and tc2.enter = 0
and tc2.exit = 1 ) EndTime,
( select min( tc2.id )
from TimeClockTable tc2
where tc.User = tc2.User
and tc.id < tc2.id
and tc2.enter = 0
and tc2.exit = 1 ) EndTimeID
from
TimeClockTable tc
where
tc.enter = 1
FEEDBACK
If the date/time stamp is always going to be sequential with the ID as it is added, ie: ID #1234 on July 5 at 10:00am will ALWAYS be before #1235 on July 5 at 10:01am (you would never have an ID 1235 or higher that was BEFORE the date/time of ID #1234), then the above modification to the query should work for you. You are already getting the lowest date/time for the given user in comparison to the first, then calling it a second time to get the minimum ID would correlate to the same end time.

There you go:
SELECT T.user_id AS User,
CAST(T.added_on AS DATE) AS Date,
DATEDIFF(
HOUR,
MIN(T.added_on),
MAX(T.added_on)
) AS TotalWorkTime
FROM WorkTable AS T
GROUP BY T.user_id,
CAST(T.added_on AS DATE)

Related

MySQL Query - data not showing as expected, problem in code

I am trying to query a database for the number of individuals who did not arrive for their booking on a given date. However, the results given are not as expected.
From manual checking, the results for 3rd May 2021 should be displayed as 3. I have a feeling that the customer id's are being added together with the result being displayed rather than just the count of individual customer id's.
select
count(c.CUSTOMER_ID) AS 'No Shows',
date(checkins.POSTDATE) as date
from
customers c, checkins
where
checkins.postdate >= date_sub(curdate(), interval 7 day)
and
(
c.archived = 0
and (
(
(
(
(
(
c.GUID in (
select
sb1.customer_guid
from
schedule_bookings sb1
join schedule_events se1 on sb1.course_guid = se1.course_guid
and sb1.OFFERING_ID in (
'2915911', '3022748', '3020740', '2915949',
'2914398', '2916147', '3022701',
'3020699', '2916185', '2915168',
'2916711', '3022403', '3020455',
'2916785', '2916478', '2915508',
'3022538', '3020582', '2915994',
'2914547', '2916069', '3022648',
'3020658', '2916107', '2915290',
'2928786', '2914729', '3022854',
'3020812', '2914694', '2914659',
'3041801', '2920756', '2920834',
'2920795', '2916223', '3022788',
'3020783', '2916239', '2915013'
)
and sb1.CANCELLED in ('0')
)
)
or (
c.GUID in (
select
sp.customer_guid
from
schedule_participants sp
join schedule_bookings sb2 on sp.BOOKING_ID = sb2.BOOKING_ID
join schedule_events se2 on sb2.course_guid = se2.course_guid
and sb2.OFFERING_ID in (
'2915911', '3022748', '3020740', '2915949',
'2914398', '2916147', '3022701',
'3020699', '2916185', '2915168',
'2916711', '3022403', '3020455',
'2916785', '2916478', '2915508',
'3022538', '3020582', '2915994',
'2914547', '2916069', '3022648',
'3020658', '2916107', '2915290',
'2928786', '2914729', '3022854',
'3020812', '2914694', '2914659',
'3041801', '2920756', '2920834',
'2920795', '2916223', '3022788',
'3020783', '2916239', '2915013'
)
and sb2.CANCELLED in ('0')
)
)
)
)
)
and (
(
(
not (
(
(
select
count(CHECKIN_ID)
from
checkins
where
checkins.CUSTOMER_ID = c.CUSTOMER_ID
) between 1
and 9999
)
)
)
)
)
)
)
and not c.customer_id in (1008, 283429, 2507795)
)
group by date(checkins.POSTDATE)
Here are the results:
+----------+------------+
| No Shows | date |
+----------+------------+
| 30627 | 2021-04-27 |
| 37638 | 2021-04-28 |
| 34071 | 2021-04-29 |
| 33579 | 2021-04-30 |
| 29274 | 2021-05-01 |
| 30135 | 2021-05-02 |
| 48339 | 2021-05-03 |
| 8979 | 2021-05-04 |
+----------+------------+
8 rows in set (8.71 sec)
As you can see, the count is nowhere near as intended.
The query parameters are:
Customer is a participant/bookee on the listed specific offerings (offering_id)
Customer's 'Check-in' count was not between 1 and 9999.
Display these results by count per date.
Can anyone see why this query would be not displaying the results as intended?
Kind Regards
Tom
Lets try to reverse this out some. You are dealing with a very finite set of Offering IDs. How about something like starting with the finite list of offerings you are concerned with and join on from that. Additionally, there does not appear to be any need for the join to the schedule events table. If something is booked, its booked. You are never getting any additional context from the event itself.
So, lets start with a very simplified union. You are looking at the bookings table for the possible customer IDs. Then from the actual participants for those same bookings. My GUESS is not every person doing the actual booking may be a participant, likewise, all participants may not be the booking party.
None of this has to do with the actual final customer, archive status or even the events for the booking. We are just getting people - period. Once you have the people and dates, then get the counts.
select
date(CI.POSTDATE) as date,
count( JustCustomers.customer_guid ) AS 'No Shows'
from
(
select
sb1.customer_guid
from
schedule_bookings sb1
where
sb1.CANCELLED = 0
-- if "ID" are numeric, dont use quotes to imply character
and sb1.OFFERING_ID in
( 2915911, 3022748, 3020740, 2915949,
2914398, 2916147, 3022701, 3020699,
2916185, 2915168, 2916711, 3022403,
3020455, 2916785, 2916478, 2915508,
3022538, 3020582, 2915994, 2914547,
2916069, 3022648, 3020658, 2916107,
2915290, 2928786, 2914729, 3022854,
3020812, 2914694, 2914659, 3041801,
2920756, 2920834, 2920795, 2916223,
3022788, 3020783, 2916239, 2915013
)
UNION
select
sp.customer_guid
from
schedule_bookings sb2
JOIN schedule_participants sp
on sb2.BOOKING_ID = sp.BOOKING_ID
where
sb2.CANCELLED = 0
and sb2.OFFERING_ID in
( 2915911, 3022748, 3020740, 2915949,
2914398, 2916147, 3022701, 3020699,
2916185, 2915168, 2916711, 3022403,
3020455, 2916785, 2916478, 2915508,
3022538, 3020582, 2915994, 2914547,
2916069, 3022648, 3020658, 2916107,
2915290, 2928786, 2914729, 3022854,
3020812, 2914694, 2914659, 3041801,
2920756, 2920834, 2920795, 2916223,
3022788, 3020783, 2916239, 2915013
)
) JustCustomers
JOIN customers c
on JustCustomers.customer_guid = c.customer_id
AND c.archived = 0
AND NOT c.customer_id IN (1008, 283429, 2507795)
JOIN checkins CI
on c.CUSTOMER_ID = CI.CUSTOMER_ID
AND CI.postdate >= date_sub(curdate(), interval 7 day)
group by
date(ci.POSTDATE)
The strange thing I notice though is that you are looking for "No shows", but explicitly looking for those people who DID check in. Now, if you are looking for all people who WERE SUPPOSED to be at a given event, then you are probably looking for where the customer DID NOT check in. If that is the intended case, there would be no check-in date to be associated. If that is the case, I would expect a date in some table such as the EVENT Date... such as going on a cruise, the event is when the cruise is, regardless of who makes it to the ship.
If I am way off, I would suggest you edit your existing post, provide additional detail / clarification.

MySQL status_id changed over time select just the latest based on the needed status

I know this sounds a little bit strange but I don't really know how to explain this better without an example.
I have the following table
ID contract_id status_id created
1 1 1 2015-10-14
2 1 2 2015-10-15
3 1 1 2016-02-02
4 1 4 2017-03-01
If the query is something like
SELECT * FROM table WHERE status_id = 1 AND created BETWEEN 2015-10-10 AND 2017-03-05
The item with contract_id = 1 should not display because the latest status in that date interval is 4
But if the query is something like this
SELECT * FROM table WHERE status_id = 1 AND created BETWEEN 2015-10-10 AND 2017-02-28 the item with contract_id 1 should show up because the latest status_id = 1
Basically what I need is something like this: Get me the latest item if the status_id = 1 at the end date
I know this is quite simple but I running around in circles right now. I did try abs(datediff(end, start)), select based on if or select in select but I am not getting the result I am looking for.
Thank you very much for your help.
One of the approaches would be to use an INNER JOIN of the latest date a given contract_id had the required status, and test if the date is earlier than the border date:
EDIT: After further reading the comments, most probably this one will do exactly what you need
SELECT t.*
FROM statuses AS t
INNER JOIN (
SELECT max(id) AS lastid, max(created) AS lastdate
FROM statuses
WHERE status_id = 1 AND created < '2016-01-01'
GROUP BY contract_id
) AS latest ON t.ID = latest.lastid
Note that it will work only if the dates are put in chronologically, in other words that for every contract_id: ID' < ID'' ≡ created' < created''
Oracle :
SELECT * FROM table WHERE status_id = 1 AND created BETWEEN 2015-10-10 AND 2017-03-05 AND ROWNUM <=1 ORDER BY created desc
MySQL :
SELECT * FROM table WHERE status_id = 1 AND created BETWEEN 2015-10-10 AND 2017-03-05 ORDER BY created desc LIMIT 1
SQL Server :
SELECT TOP 1 FROM table WHERE status_id = 1 AND created BETWEEN 2015-10-10 AND 2017-03-05 ORDER BY created desc
I understood your question as that you want an item with status_id = 1 and which has most recent created date

Mysql Unique records, where multiple records exist

I am struggling with a Mysql call and was hoping to borrow your expertise.
I believe that what I want may only be possible using two selects and I have not yet done one of these and am struggling to wrap my head around this.
I have a table like so:
+------------------+----------------------+-------------------------------+
| username | acctstarttime | acctstoptime |
+------------------+----------------------+-------------------------------+
| bill | 22.04.2014 | 23.04.2014 |
+------------------+----------------------+-------------------------------+
| steve | 16.09.2014 | |
+------------------+----------------------+-------------------------------+
| fred | 12.08.2014 | |
+------------------+----------------------+-------------------------------+
| bill | 24.04.2014 | |
+------------------+----------------------+-------------------------------+
I wish to select only unique records from the username column ie I only want one record for bill and I need the one with most recent start_date, providing they were weren't in the last three months (end_date is not important to me here) else I do not want any data. In summary I just need anyone where there most recent start date is over 3 months old.
The command I am using currently is:
SELECT DISTINCT(username), ra.acctstarttime AS 'Last IP', ra.acctstoptime
FROM radacct AS ra
WHERE ra.acctstarttime < DATE_SUB(now(), interval 3 month)
GROUP BY ra.username
ORDER BY ra.acctstarttime DESC
However, this simply gives me details about the date_start for that particular customer where they had a start date over 3 months ago.
I have tired a few other combinations of this and have tried a command with a double select but I'm currently hitting brick walls. Any help or a push in the right direction would be much appreciated.
Update
I have created the following:
http://sqlfiddle.com/#!2/f47b2/1
Effectively I should only see 1 row when the query is as it should be. This would be the row for bill. As he is the only one that does not have a start date within the last three months. The result I would expect to see is the following:
24 bill April, 11 2014 12:11:40+0000 (null)
As this is the latest start date for bill, but this start date is not within the last three months. Hopefully this will help clarify. Many thanks for your help thus far.
http://sqlfiddle.com/#!2/f47b2/14
This is another example. If the acctstartdate for bill would show as the April entry, then I could add my where clause for the last three months and this would give me my desired result.
SQLFiddle
http://sqlfiddle.com/#!2/444432/9 (MySQL 5.5)
I am looking at the question in 2 ways based on the current text:
I only want one record for bill and I need the one with most recent start_date, providing they were in the last three months (end_date is not important to me here) else I do not want any data
Structure
create table test
(
username varchar(20),
date_start date
);
Data
Username date_start
--------- -----------
bill 2014-09-25
bill 2014-09-22
bill 2014-05-26
andy 2014-05-26
tim 2014-09-25
tim 2014-05-26
What we want
Username date_start
--------- -----------
bill 2014-09-25
tim 2014-09-25
Query
select *
from test a
inner join
(
select username, max(date_start) as max_date_start
from test
where date_start > date_sub(now(), interval 3 month)
group by username
) b
on
a.username = b.username
and a.date_start = b.max_date_start
where
date_start > date_sub(now(), interval 3 month)
Explanation
For the most recent last 3 months, let's get maximum start date for each user. To limit the records to the latest 3 months we use where date_start > date_sub(now(), interval 3 month) and to find the maximum start date for each user we use group by username.
We, then, join main data with this small subset based on user and max date to get the desired result.
Another angle
If we desire to NOT look at the latest 3 months and instead find the most recent date for each user, we would be looking at this kind of data:
What we want
Username date_start
--------- -----------
bill 2014-05-26
tim 2014-05-26
andy 2014-05-26
Query
select *
from test a
inner join
(
select username, max(date_start) as max_date_start
from test
where date_start < date_sub(now(), interval 3 month)
group by username
) b
on
a.username = b.username
and a.date_start = b.max_date_start
where
date_start < date_sub(now(), interval 3 month)
Hopefully you can change these queries to your liking.
EDIT
Based on your good explanation, here's the query
SQLFiddle: http://sqlfiddle.com/#!2/f47b2/17
select *
from activity a
-- find max dates for users for records with dates after 3 months
inner join
(
select username, max(acctstarttime) as max_date_start
from activity
where acctstarttime < date_sub(now(), interval 3 month)
group by username
) b
on
a.username = b.username
and a.acctstarttime = b.max_date_start
-- find usernames who have data in the recent three months
left join
(
select username, count(*)
from activity
where acctstarttime >= date_sub(now(), interval 3 month)
group by username
) c
on
a.username = c.username
where
acctstarttime < date_sub(now(), interval 3 month)
-- choose users who DONT have data from recent 3 months
and c.username is null
Let me know if you would like me to add explanation
Try this:
select t.*
from radacct t
join (
select ra.username, max(ra.acctstarttime) as acctstarttime
from radacct as ra
WHERE ra.acctstarttime < DATE_SUB(now(), interval 3 month)
) s on t.username = s.username and t.acctstarttime = s.acctstarttime
SQLFiddle

MySQL: query with condition on one-to-many table

I have a table with schema like this:
clients_actions
id | client_id | action | date_done
1 | 1 | ... | 1394785392
2 | 2 | ... | 1394786392
3 | 2 | ... | 1394787392
date_done can be set both in the past, and in the future from current unix timestamp. I need to select all 'forgotten' clients, which don't have date_done set in future (in all his actions) and last his action is older than 604800 seconds (7 days). Client can have many actions. And also, if it's possible, I need in the same query to select his last action (which is in past and more than 7 days old).
How can it be done?
One way to do it as
select * from clients_actions
where from_unixtime(date_done) < date_sub(now(),INTERVAL 7 day)
AND client_id
NOT IN
(
select client_id from
clients_actions
where from_unixtime(date_done) > now()
)
;
DEMO
In the demo I have added some data with future dates so that they can be ignored and just by getting data older than 7 days. You can do group by in case there are repeated data in your table.
Select client_id, action, MAX(date_done) from clients_actions
WHERE date_done < (UNIX_TIMESTAMP(SYSDATE() - 7)
AND id NOT IN (SELECT id FROM clients_actions
WHERE date_done > (UNIX_TIMESTAMP(SYSDATE()))
GROUP BY client_id;
For the first part you want a query that has Where date_done < SysDate - 7 days and client_id not in (select id from clients_actions where date_done > SysDate (also converted to UNIX). This says I want all records whose date_done is older than 7 days ago, but that don't have any actions due in the future.
the MAX and group by client_id limit it to only the latest record of those selected by client_id.
The following query will get you the desired result.
SELECT *
FROM clients_actions ca
INNER JOIN
(SELECT client_id, MAX(date_done) as date_done
FROM clients_actions
WHERE DATEDIFF(CURRENT_TIMESTAMP, FROM_UNIXTIME(date_done)) >= 7
GROUP BY client_id) latest_date
ON ca.client_id = latest_date.client_id AND ca.date_done = latest_date.date_done;

SQL: how to get last items?

I have a log for documents that go through my application. The log looks like this:
TABLE: log
==================================================
| log_id | document_id | status_code | timestamp |
==================================================
| 1 | 10 | 100 | 12345 |
--------------------------------------------------
| 2 | 10 | 200 | 23456 |
--------------------------------------------------
I need a list of document_id that have been "stuck" in a certain status_code for a given duration (say 10 minutes; timestamp is Unix timestamp, btw). If a particular document_id is "stuck" in a certain status_code, that status_code will be the last status_code for that document_id.
How do I query this? The 2 things I'm not sure of:
How do I check if the document has been in a certain status for a certain period of time? I'm guessing I can use some sort of function/formula in my SQL (e.g. now - 10 minutes), but I don't know how to do that.
How do I check the duration of only the last status code?
SELECT log.document_id
, (UNIX_TIMESTAMP() - log.timestamp) / 60
AS MinutesSinceLastChange
FROM log
JOIN
( SELECT document_id
, MAX(timestamp) AS last_change
FROM log
GROUP BY document_id
HAVING (last_change < (UNIX_TIMESTAMP() - 60 * 10)) <-- that is 10 minutes
) AS grp
ON grp.document_id = log.document_id
AND grp.last_change = log.timestamp
WHERE log.status_code = "200" <-- code you want to check
For Your no. one and no. two question, this is my proposition for (I guess You are using MySQL):
SELECT
`document_id`,
SUBSTR(GROUP_CONCAT(RPAD(`status_code`,5) ORDER BY `timestamp` DESC), 1, 5) AS `last_status`,
SUBSTR(GROUP_CONCAT(RPAD(`status_code`,5) ORDER BY `timestamp` DESC), 7, 5) AS `prev_status`,
UNIX_TIMESTAMP(SUBSTR(GROUP_CONCAT(FROM_UNIXTIME(`timestamp`) ORDER BY `timestamp` DESC), 1, 19)) AS `last_timestamp`,
UNIX_TIMESTAMP(SUBSTR(GROUP_CONCAT(FROM_UNIXTIME(`timestamp`) ORDER BY `timestamp` DESC), 21, 19)) AS `prev_timestamp`
FROM `log`
GROUP BY `document_id`
HAVING `last_timestamp` - `prev_timestamp` > 60*10 AND `last_status` IN (100,200);
All right, what is happing there. We're grouping rows by document_id, and ordering status codes and timestamps inside GROUP_CONCAT to get the last and pre-last entry.
If Your status code can have more than 5 digits, then replace it RPAD(status_code,X), where X is maximal number of status_code length
60*10 - is 10 minutes
last_status IN (100,200) - status codes You want to get only.
SELECT log.document_id, log.status_code, max(log.timestamp) - min(log.timestamp)
FROM (
SELECT MAX(log_id) as log_id
FROM log
GROUP BY document_id) latestLog
INNER JOIN log latestStatus ON latestStatus.log_id = latestLog.log_id
INNER JOIN log on latestStatus.status_code = log.status_code
GROUP BY log.document_id, log.status_code
HAVING (max(log.timestamp) - min(log.timestamp)) > 600