How to write a script for count working time employee? - mysql

help me, please!
At the table - date, time, person, source. Updated with new values ​​when employee passing through the checkpoint, he can leave / came several times per day.
+---------------+----------+--------+-------------+
| date | time |person |source |
+---------------+----------+--------+-------------+
| 01.08.2014 | 08:42:08 | Name1 | enter1 |
+---------------+----------+--------+-------------+
| 01.08.2014 | 09:42:12 | Name1 | exit1 |
+---------------+----------+--------+-------------+
| 01.08.2014 | 10:22:45 | Name1 | enter2 |
+---------------+----------+--------+-------------+
| 01.08.2014 | 18:09:11 | Name1 | exit2 |
+---------------+----------+--------+-------------+
I need to count for each employee the actual time he spent at work each day. Table will always be not editable. It is formed from a csv file. The script runs once.
I think need to do something like this:
TIMESTAMPDIFF(MINUTE, enterTime, exitTime)
for each employee for 1 day. But I have a very poor knowledge in sql.

The date/time formats should be stored in a datetime/timestamp column. It is possible to convert them, although ugly (there's probably a better way...):
> SELECT CONCAT(STR_TO_DATE('01.08.2014', '%m.%d.%Y'), ' ', '08:42:08');
2014-01-08 08:42:08
Now Suppose the times are unix timestamps. An employ arrives at t0 and leaves at t1. The time he was at work is (t1-t0) seconds. Now suppose he he arrives at t0, leaves for a break at t1, returns at t2, and leaves for the day at t3. His total time at work is (t1-t0) + (t3-t2) = (t1+t3)-(t0+t2). In general: his time at work for a given day is the sum of the arrival times subtracted from the sum of the departure times.
Using your times:
1389188528 enter1
1389192132 exit1
1389194565 enter2
1389222551 exit2
We see that total time at work is: 1389222551 + 1389192132 - (1389188528 + 1389194565) = 31590, or about 8 hours and 47 minutes. Now what remains is converting to unix timestamps (UNIX_TIMESTAMP()) and applying this reasoning via SQL. In the following example, I have added your data to a table named work_log and assumed that when source begins with exit or enter, we are respectively referring to a departure or arrival.
SELECT person, DATE(dt) AS day,
SUM(IF(`source` like 'enter%', -1, 1)*UNIX_TIMESTAMP(dt))/3600 AS hours
FROM (SELECT CONCAT(STR_TO_DATE(`date`, '%m.%d.%Y'), ' ', `time`) AS `dt`,
`person`,`source` FROM work_log) AS wl
GROUP BY person,day;
+--------+------------+--------------+
| person | day | hours |
+--------+------------+--------------+
| Name1 | 2014-01-08 | 8.7750000000 |
+--------+------------+--------------+
1 row in set (0.00 sec)
There are probably cleaner ways of doing that.

Related

Is there a way to group by one minute periods in MySQL using unix timestamps?

I have a MariaDB database holding some data for a game and I wanted to use Grafana to graph out how many rows have been inserted every minute. I'm using a Node.js script to fetch the data every minute from the game's Public API and insert it into my db, however I can't figure out how to get MySQL to output something that works with Grafana. What I need it to do is output a table with a column for the number of new inserts and the time at the start of the one minute window. My DB looks something like this:
--------------------------------------------------------------------
| auction_id | seller | buyer | timestamp | price | bin | item_bytes |
--------------------------------------------------------------------
I have a working query for returning the number of new inserts for the current data supplied by the API, however I can't figure out how to get the historical data for the past minutes. The output I'm looking for would look something like this:
-----------------------------
| new inserts | timestamp |
-----------------------------
| 234 | 1625373706053 |
-----------------------------
| 684 | 1625373666053 |
-----------------------------
| 720 | 1625373626053 |
-----------------------------
| 403 | 1625373586053 |
-----------------------------
Notice how the timestamp goes down by 60,000 every row, which is equivalent to 60 seconds, or a minute. I have tried using GROUP BY and almost every other solution I could find on StackOverflow and other sites, however it still doesn't work.
Please don't hesitate to comment if I wasn't clear enough.
You may use UNIX_TIMESTAMP to convert your milliseconds-since-epoch values into a bona fide datetime. Then, aggregate by minute to get the counts:
SELECT
COUNT(*) AS `new inserts`,
FROM_UNIXTIME(timestamp / 1000, '%Y-%m-%d %H:%i') AS ts_minute
FROM yourTable
GROUP BY ts_minute
ORDER BY ts_minute;

Self Join? Were Staff Who Worked the Previous Week Active 3 Weeks ago - MYSQL

I'm trying to add a column to a production hours dataset that will tell if a provider who worked last week was also working three weeks earlier. The current dataset looks something like this:
RowID | ProviderID | ClientID | DOS | DOS (Week) | Hours
1 | 1111111111 | 22222222 | 11/2/2020 | 11/1/2020 | 2.5
2 | 1111111111 | 33333333 | 11/5/2020 | 11/1/2020 | 1
3 | 1111111111 | 44444444 | 10/13/2020 | 10/11/2020 | 3
I'm trying to get an extra column 'Active 3 Weeks Prior' with y/n or 1/0 for values. For the above table, let's assume the provider started on 10/13/20. The new column would ideally populate like this:
RowID | ProviderID | ClientID | DOS | DOS (Week) | Hours | Active 3 weeks Prior
1 | 1111111111 | 22222222 | 11/2/2020 | 11/1/2020 | 2.5 | Yes
2 | 1111111111 | 33333333 | 11/5/2020 | 11/1/2020 | 1 | Yes
3 | 1111111111 | 44444444 | 10/13/2020 | 10/11/2020 | 3 | No
A couple extra tidbits: our org uses Sunday as the start of the week so DOS (Week) is the Sunday prior to the date of service. From what I've been reading so far, it seems like the solution here is some kind of self join, where the base production records are aggregated into weekly hours and compared with that same providerID's records for DOS (Week) - 21.
The trouble I'm having is: whether I'm on the right track in the first place with the self-join and how I would generate the y/n values based on the success or failure to find a matching value. Also, I suspect that joining based on a concatenate of ProviderID and DOS(Week) might be flawed? This is what I've been playing with so far.
Please let me know if I can clarify the question at all or am missing something very obvious. I truly appreciate any help, as I've been trying to figure out the right search terms to get a clue on the answer for a few days now.
If you are running MySQL 8.0, you can use window functions and a range specification:
select t.*,
(
max(providerid) over(
partition by providerid
order by dos
range between interval 3 week preceding and interval 3 week preceding
) is not null
) as active_3_weeks_before
from mytable t
It is not really clear from your explanation and data what you mean by was also working three weeks earlier. What the query does is, for each row, to check if another row exists with the same supplier and a dos that is exactly 3 week before the dos of the current row. This can easily be adapted for some other requirement.
Edit: if you want to check for any record within the last 3 weeks, you would change the window range to:
range between interval 3 week preceding and interval 1 day preceding
And if you want this in MySQL < 8.0, where window functions are not available, then you would use a correlated subquery:
select t.*,
exists (
select 1
from mytable t1
where
t1.providerid = t.provider_id
and t1.dos >= t.dos - interval 3 week
and t1.dos < t.dos
) as active_3_weeks_before
from mytable t

Use Max date to create a date range

I need to create a date range in a table that houses transaction information. The table updates sporadically throughout the week from a manual process. Each time the table is updated transactions are added up to the previous Sunday. For instance, the upload took place yesterday and so transactions were loaded through last Sunday (Feb 26th). If it had been loaded on Wednesday it would still be dated for Sunday. The point is that I have a moving target with my transactions and also when the data is loaded to the table. I am trying to fix my look back period to the date of the latest transaction then go three weeks back. Here is the query that I came up with:
SELECT distinct TransactionDate
FROM TransactionTABLE TB
inner join (
SELECT distinct top 21 TransactionDate FROM TrasactionTABLE ORDER BY TransactionDate desc
) A on TB.TransactionDate = A.TransactionDate
ORDER BY TB.TransactionDate desc
Technically this code works. The problem that I am running into now is when there were no transactions on a given date, such as bank holidays (in this case Martin Luther King Day), then the query looks back one day too far.
I have tried a few different options including MAX(TransactionDate) but if I use that in a sub-query or CTE then use the new value in a WHERE statement as a reference I only get the max value or the value I subtract that statement by. For instance if I say WHERE TransactionDate >= MAX(TransactionDate)-21 and the max date is Feb 26th then the result is Feb 2nd instead of the range of dates from Feb 2nd through Feb 26th.
IN SUMMARY, what I need is a date range looking three weeks back from the date of the latest transaction date. This is for a daily report so I cannot hardcode the date in. Since I am also using Excel Connections the use of Declare statements is prohibited.
Thank you StackOverflow gurus in advance!
You could use something like this:
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
select top (21)
[Date]=convert(date,dateadd(day, row_number() over (order by (select 1))-1
, dateadd(day,-20,(select max(TransactionDate) from t) ) ) )
from n as deka
cross join n as hecto
order by [Date]
)
select Date=convert(varchar(10),dates.date,120) from dates
rextester demo: http://rextester.com/ZFYV25543
returns:
+------------+
| Date |
+------------+
| 2017-02-06 |
| 2017-02-07 |
| 2017-02-08 |
| 2017-02-09 |
| 2017-02-10 |
| 2017-02-11 |
| 2017-02-12 |
| 2017-02-13 |
| 2017-02-14 |
| 2017-02-15 |
| 2017-02-16 |
| 2017-02-17 |
| 2017-02-18 |
| 2017-02-19 |
| 2017-02-20 |
| 2017-02-21 |
| 2017-02-22 |
| 2017-02-23 |
| 2017-02-24 |
| 2017-02-25 |
| 2017-02-26 |
+------------+
I just found this for looking up dates that fall within a given week. The code can be manipulated to change the week start date.
select convert(datetime,dateadd(dd,-datepart(dw,convert(datetime,convert(varchar(10),DateAdd(dd,-1/*this # changes the week start day*/,getdate()),101)))+1/*this # is used to change the week start date*/,
convert(datetime,convert(varchar(10),getdate(),21))))/*also can enter # here to change the week start date*/
I've included a screenshot of the results if you were to include this with a full query. This way you can see how it looks with a range of dates. I did a little manipulation so that the week starts on Monday and references Monday's date.
Since I am only looking back three weeks a simple GETDATE()-21 is sufficient because as the query moves forward through the week it will look back 21 days and pick the Monday at the beginning of the week as my start date.

MySQL Select rows having sum(criteria1) <> sum(criteria2)

I'm sure the answer is out there somewhere, but can't find it...
One table stores logged hours for employees, classified by type (type = Project or Task).
For each day, I can have hours logged on multiple tasks.
By doing a simple sum and grouping I can get the total hours per type and per day, but I'd like to go one step further:
display only the "error cases" where sum(hours) per Project is different than sum(hours) for all Tasks for a given date.
mysql> select sum(logged), type, date from `hoursLog` group by idEmployee, date, type;
+-------------+----------+----------+
| sum(logged) | type | date |
+-------------+----------+----------+
| 0.8 | Project | 20160525 |
| 1.0 | Task | 20160525 |
| 0.3 | Project | 20160526 |
| 0.3 | Task | 20160526 |
| 0.3 | Project | 20160527 |
| 0.5 | Task | 20160527 |
+-------------+----------+----------+
From the above table, I want only the dates 20160525 and 20160527, for which the sum is different.
Appreciate any help!
SELECT
SUM(IF(`type`='Task',logged,0)) task_hours,
SUM(IF(`type`='Project',logged,0)) project_hours,
date
FROM `hoursLog`
GROUP by idEmployee, date
HAVING task_hours <> project_hours
But I am not sure if you really need GROUP BY idEmployee. I guess this works now just because you have few records in test DB with only 1 employee involved. Do you need time summarized per person? or just per date?

Auto Increment mysql trigger

How create a Auto increment field based on this example:
I have this table, with "AF" field, in that format: SN.MM.YYYY
The "SN" = AI number based on last insert, MM= Atual Month, YYYY = Atual Year.
| ID | AF |
____________________
| 1 | 01.10.2013 |
| 2 | 02.10.2013 |
So, when changes the month or year, the trigger must set "AF" field that way:
Ex.: Month changes to November(Reset SN to 01).
| 3 | 01.11.2013 |
| 4 | 02.11.2013 |
The same thing when year changes(Reset SN to 01):
| 5 | 01.01.2014 |
| 6 | 02.01.2014 |
| 7 | 03.01.2014 |
Anyone know's how set that trigger?
Obs: There may be more than one record in one day, so, day is not important.
Sorry for the bad english
Thanks guys!
Technically you can do something like this
CREATE TRIGGER tg_bi_table1
BEFORE INSERT ON table1
FOR EACH ROW
SET NEW.af = CONCAT(
LPAD(COALESCE(
(SELECT MAX(LEFT(af, 2))
FROM table1
WHERE af LIKE DATE_FORMAT(CURDATE(), '__.%m.%Y')), 0) + 1, 2, '0'),
DATE_FORMAT(CURDATE(), '.%m.%Y'));
Here is SQLFiddle demo
Note: This approach (creating your own ago_increment values with such a pattern) has two major drawbacks:
Under heavy concurrent access different connections may obtain the same AF number
Because of your particular AF pattern (SN comes first) using an index is impossible therefore you'll end up always getting a full scan