calculate work hours in each day using mysql - mysql

I want to calculate work hours in each day using MySQL. I show many hours calculate solution but none of them fit to my requirement. In my table I don't have in our out field. I have to consider first entry as in and second as out and calculate working hours according to it.
Table structure:
CREATE TABLE IF NOT EXISTS `timesheet` (
`MachineNo` int(6) unsigned NOT NULL,
`Empcardno` int(3) unsigned NOT NULL,
`Date` date NOT NULL,
`Time` time NOT NULL
) DEFAULT CHARSET=utf8;
INSERT INTO `timesheet`
(`MachineNo`, `Empcardno`, `Date`,`Time`) VALUES
(01, 5, '2020-05-22', '18:15:54'),
(01, 5, '2020-05-22', '14:46:47'),
(01, 5, '2020-05-22', '14:26:05'),
(01, 5, '2020-05-22', '09:26:30'),
(01, 5, '2020-05-21', '18:15:45'),
(01, 5, '2020-05-21', '14:48:39'),
(01, 5, '2020-05-21', '14:29:55'),
(01, 5, '2020-05-21', '09:37:49');
MySql fiddle link
I have tried the following query but it gives me only total hours between max and min time. It does not consider all the in out values. I want to consider time between all in and out point.
SELECT
Empcardno,min(Time),
max(Time),
TIMEDIFF(max(Time),
min(Time)) As Diff_Value
FROM
timesheet
GROUP BY
DATE(Date),Empcardno
Result
Empcardno Date min(Time) max(Time) Diff_Value
5 2020-05-21 09:37:49 18:15:45 08:37:56
5 2020-05-22 09:26:30 18:15:54 08:49:24
But i want to calculate time as time between this two
(01, 5, '2020-05-21', '14:29:55'),
(01, 5, '2020-05-21', '09:37:49');
and
(01, 5, '2020-05-21', '18:15:45'),
(01, 5, '2020-05-21', '14:48:39'),

This is a tricky problem to solve without window functions... Basically you need to generate row numbers for each entry for each machine on each date. Then you can combine the odd and even rows to generate a time difference which can be summed to generate the total time for the day. To do this, I've converted the times on odd rows to negative, so that when that value is added to the next value (from the even row), we get the difference between the two.
SELECT MachineNo, Empcardno, Date, SEC_TO_TIME(SUM(tsecs)) AS total_time
FROM (
SELECT CASE WHEN MachineNo = #mn AND Empcardno = #en AND `Date` = #dt
THEN #rn := #rn + 1
ELSE #rn := 1
END AS rn,
#mn := MachineNo AS MachineNo, #en := Empcardno AS Empcardno,
#dt := `Date` AS `Date`,
CASE WHEN #rn % 2 = 1 THEN -TIME_TO_SEC(`Time`)
ELSE TIME_TO_SEC(`Time`)
END AS tsecs
FROM timesheet
CROSS JOIN (SELECT #mn := 0, #en := 0, #dt := '', #rn := 0) init
ORDER BY Date, Time
) t
GROUP BY MachineNo, Empcardno, Date
Output:
MachineNo Empcardno Date total_time
1 5 2020-05-21 08:19:12
1 5 2020-05-22 08:28:42
Demo on SQLFiddle

Related

How can I check streaks by team and by result using mySQL?

I have created a View that includes the match id, team, result (using "W", "D", "L") and date.
e.g.:
'32750', 'Team 1', 'D', '2019-02-16 21:30:00'
'32750', 'Team 2', 'D', '2019-02-16 21:30:00'
'32748', 'Team 3', 'L', '2019-02-16 19:20:00'
'32748', 'Team 4', 'W', '2019-02-16 19:20:00'
I've adapted a query I found here that calculates streaks using that table and shows me the Team, Start date of the streak, end date, streak count and the matches that are included in that streak.
'Team 1', '1992-07-05', '1992-09-06', '5', '9522,9142,9161,9167,9180'
The problem I have is that I have to include the team name in a where clause or the query shows incorrect results. Here is the query I'm using for Winning Streaks
select
team_name,
min(match_date) as start_date,
max(match_date) as end_date,
count(match_date) as streak,
group_concat(idmatch) as gameid_list
from
(
select *,
IF(
result = "W"
and
#result = "W",
#gn, #gn := #gn + 1)
as group_number,
#match_date as old_date, #idmatch as old_gameid,
#result as old_rersult,
#match_date := match_date, #idmatch := idmatch,
#result := result
from my_football_database.`Resultados Globales WDL`
cross join
(
select
#match_date := CAST(null as date) as xa,
#idmatch := null + 0 as xb,
#result := null + 0 as xc, #gn := 0
) x where team_name = "Team 1"
order by match_date
) as y
group by group_number
order by streak desc;
As you can see, I have to specify the name of the team in a where clause so If I want to get for example the top 3 winning streaks in history, I have to run this query 150 times for each team, and then manually compare.
How can I modify this to get a full list of streaks correctly grouped by teams?

Finding in between time in a list of times

I have a table that looks like this
userid | eventid | description | date | starttime | endtime
1 1 Event 1 2016-02-02 09:30:00 11:00:00
1 2 Event 2 2016-02-02 13:30:00 15:00:00
1 3 Event 3 2016-02-02 17:30:00 21:00:00
2 4 Event 4 2016-02-03 13:00:00 14:00:00
2 5 Event 5 2016-02-03 15:00:00 16:00:00
I need to find what is the sum of time between the events on the same day by the user.
Like this:
userid | timeBetween
1 05:00:00
2 01:00:00
I should also assume that there may be overlapping times for example event1 starts at 11:00 ends 13:00 and event2 starts 12:00 and ends 14:00 by the same user on the same day. These cases are rare and I believe returning 00:00 here is the appropriate answer.
I solved a similar problem, finding the sum of the length of all events per day.
SELECT *,
SEC_TO_TIME( SUM( TIME_TO_SEC(TIMEDIFF(`endtime`,`starttime`)))) as sumtime
FROM `events`
group by userid, date
order by sumtime desc
Given this sample data:
CREATE TABLE t
(`userid` int, `eventid` int, `description` varchar(7), `date` date, `starttime` time, `endtime` time)
;
INSERT INTO t
(`userid`, `eventid`, `description`, `date`, `starttime`, `endtime`)
VALUES
(1, 1, 'Event 1', '2016-02-02', '09:30:00', '11:00:00'),
(1, 2, 'Event 2', '2016-02-02', '13:30:00', '15:00:00'),
(1, 3, 'Event 3', '2016-02-02', '17:30:00', '21:00:00'),
(2, 4, 'Event 4', '2016-02-03', '13:00:00', '14:00:00'),
(2, 5, 'Event 5', '2016-02-03', '15:00:00', '16:00:00')
;
this query
SELECT userid, SEC_TO_TIME(SUM(TIME_TO_SEC(diff))) AS time_between
FROM (
SELECT
TIMEDIFF(starttime, COALESCE(IF(userid != #prev_userid, NULL, #prev_endtime), starttime)) AS diff,
#prev_endtime := endtime,
#prev_userid := userid AS userid
FROM
t
, (SELECT #prev_endtime := NULL, #prev_userid := NULL) var_init_subquery
ORDER BY userid
) sq
GROUP BY userid;
will return
+--------+--------------+
| userid | time_between |
+--------+--------------+
| 1 | 05:00:00 |
| 2 | 01:00:00 |
+--------+--------------+
Explanation:
In this part
, (SELECT #prev_endtime := NULL, #prev_userid := NULL) var_init_subquery
ORDER BY userid
we initialize our variables. The ORDER BY is very important, since there's no order in a relational database unless you specify it. It is so important, because the SELECT clause processes the rows in this order.
In the SELECT clause the order is also very important. Here
#prev_endtime := endtime,
#prev_userid := userid AS userid
we assign the values of the current row to the variables. Since this happens after this line
TIMEDIFF(starttime, COALESCE(IF(userid != #prev_userid, NULL, #prev_endtime), starttime)) AS diff,
the variables still hold the values of the previous row in the timediff() function. Therefore we also have to use COALESCE(), because in the very first row and when the userid changes, there is no value to calculate the diff from. To get a diff of 0 there, COALESCE() exchanges the NULL value with the starttime.
The last part is obviously to simply sum the seconds of the "between times".
Here's one way you can get the timeBetween value in SECONDS
SELECT
firsttable.userid,
SEC_TO_TIME(SUM(TIME_TO_SEC(secondtable.starttime) - TIME_TO_SEC(firsttable.endtime))) timeBetween
FROM
(
SELECT
*,
IF(#prev = userid, #rn1 := #rn1 + 1, #rn1 := 1) rank,
#prev := userid
FROM eventtable,(SELECT #prev := 0,#rn1 := 1) var
ORDER BY userid,starttime DESC
) firsttable
INNER JOIN
(
SELECT
*,
IF(#prev2 = userid, #rn2 := #rn2 + 1, #rn2 := 1) rank,
#prev2 := userid
FROM eventtable,(SELECT #prev2 := 0,#rn2 := 1) var
ORDER BY userid,endtime DESC
) secondTable
ON firsttable.userid = secondtable.userid AND firsttable.rank = secondtable.rank + 1 AND
firsttable.date = secondtable.date
GROUP BY firsttable.userid;
TEST:
Unable to add a fiddle.
So here's test data with schema:
DROP TABLE IF EXISTS `eventtable`;
CREATE TABLE `eventtable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userid` int(11) NOT NULL,
`eventid` int(11) NOT NULL,
`description` varchar(100) CHARACTER SET utf8 NOT NULL,
`date` date NOT NULL,
`starttime` time NOT NULL,
`endtime` time NOT NULL,
PRIMARY KEY (`id`)
) ;
INSERT INTO `eventtable` VALUES ('1', '1', '1', 'Event 1', '2016-02-02', '09:30:00', '11:00:00');
INSERT INTO `eventtable` VALUES ('2', '1', '2', 'Event 2', '2016-02-02', '13:30:00', '15:00:00');
INSERT INTO `eventtable` VALUES ('3', '1', '3', 'Event 3', '2016-02-02', '17:30:00', '21:00:00');
INSERT INTO `eventtable` VALUES ('4', '2', '4', 'Event 4', '2016-02-03', '13:00:00', '14:00:00');
INSERT INTO `eventtable` VALUES ('5', '2', '5', 'Event 5', '2016-02-03', '15:00:00', '16:00:00');
Result:
Executing the above query on the given test data you will get output like below:
userid timeBetween
1 05:00:00
2 01:00:00
Note:
For overlapping events the above query will give you negative timeBetween value.
You can replace the the SEC_TO_TIME...line by the following:
SEC_TO_TIME(IF(SUM(TIME_TO_SEC(secondtable.starttime) - TIME_TO_SEC(firsttable.endtime)) < 0, 0,SUM(TIME_TO_SEC(secondtable.starttime) - TIME_TO_SEC(firsttable.endtime)))) timeBetween
If you take the TIMEDIFF of the MIN(starttime) and MAX(endtime) for each user/day and then subtract the sum of events as calculated earlier, this will give you the times in between.
try this on
select TIMEDIFF('start_time','end_time') from your table
hope this one help you

Count consecutive rows with a particular status

I need to count whether there are three consecutive failed login attempts of the user in last one hour.
For example
id userid status logindate
1 1 0 2014-08-28 10:00:00
2 1 1 2014-08-28 10:10:35
3 1 0 2014-08-28 10:30:00
4 1 0 2014-08-28 10:40:00
In the above example, status 0 means failed attempt and 1 means successful attempt.
I need a query that will count three consecutive records of a user with status 0 occurred in last one hour.
I tried below query
SELECT COUNT( * ) AS total, Temp.status
FROM (
SELECT a.status, MAX( a.id ) AS idlimit
FROM loginAttempts a
GROUP BY a.status
ORDER BY MAX( a.id ) DESC
) AS Temp
JOIN loginAttempts t ON Temp.idlimit < t.id
HAVING total >1
Result:
total status
2 1
I don't know why it display status as 1. I also need to add a where condition on logindate and status field but don't know how would it work
For consecutive count you can use user defined variables to note the series values ,like in below query i have use #g and #r variable, in inner query i am storing the current status value that could be 1/0 and in case expression i am comparing the value stored in #g with the status column if they both are equal like #g is holding previous row value and previous row's status is equal to the current row's status then do not change the value stored in #r,if these values don't match like #g <> a.status then increment #r with 1, one thing to note i am using order by with id column and assuming it is set to auto_increment so for consecutive 1s #r value will be same like #r was 3 for first status 1 and the again status is 1 so #r will 3 until the status changes to 0 same for status 0 vice versa
SELECT t.userid,t.consecutive,t.status,COUNT(1) consecutive_count
FROM (
SELECT a.* ,
#r:= CASE WHEN #g = a.status THEN #r ELSE #r + 1 END consecutive,
#g:= a.status g
FROM attempts a
CROSS JOIN (SELECT #g:=2, #r:=0) t1
WHERE a.`logindate` BETWEEN '2014-08-28 10:00:00' AND '2014-08-28 11:00:00'
ORDER BY id
) t
GROUP BY t.userid,t.consecutive,t.status
HAVING consecutive_count >= 3 AND t.status = 0
Now in parent query i am grouping results by userid the resultant value of case expression i have name is it as consecutive and status to get the count for each user's consecutive status
One thing to note for above query that its necessary to provide the
hour range like i have used between without this it will be more
difficult to find exactly 3 consecutive statuses with in an hour
Sample data
INSERT INTO attempts
(`id`, `userid`, `status`, `logindate`)
VALUES
(1, 1, 0, '2014-08-28 10:00:00'),
(2, 1, 1, '2014-08-28 10:10:35'),
(3, 1, 0, '2014-08-28 10:30:00'),
(4, 1, 0, '2014-08-28 10:40:00'),
(5, 1, 0, '2014-08-28 10:50:00'),
(6, 2, 0, '2014-08-28 10:00:00'),
(7, 2, 0, '2014-08-28 10:10:35'),
(8, 2, 0, '2014-08-28 10:30:00'),
(9, 2, 1, '2014-08-28 10:40:00'),
(10, 2, 1, '2014-08-28 10:50:00')
;
As you can see from id 3 to 5 you can see consecutive 0s for userid 1 and similarly id 6 to 8 userid 2 has consecutive 0s and they are in an hour range using above query you can have results as below
userid consecutive status consecutive_count
------ ----------- ------ -------------------
1 2 0 3
2 2 0 3
Fiddle Demo
M Khalid Junaid's answer is great, but his Fiddle Demo didn't work for me when I clicked it.
Here is a Fiddle Demo which works as of this writing.
In case it doesn't work later, I used the following in the schema:
CREATE TABLE attempts
(`id` int, `userid` int, `status` int, `logindate` datetime);
INSERT INTO attempts
(`id`, `userid`, `status`, `logindate`)
VALUES
(1, 1, 0, '2014-08-28 10:00:00'),
(2, 1, 1, '2014-08-28 10:10:35'),
(3, 1, 0, '2014-08-28 10:30:00'),
(4, 1, 0, '2014-08-28 10:40:00'),
(5, 1, 0, '2014-08-28 10:50:00'),
(6, 2, 0, '2014-08-28 10:00:00'),
(7, 2, 0, '2014-08-28 10:10:35'),
(8, 2, 0, '2014-08-28 10:30:00'),
(9, 2, 1, '2014-08-28 10:40:00'),
(10, 2, 1, '2014-08-28 10:50:00')
;
And this as the query:
SELECT t.userid,t.consecutive,t.status,COUNT(1) consecutive_count
FROM (
SELECT a.* ,
#r:= CASE WHEN #g = a.status THEN #r ELSE #r + 1 END consecutive,
#g:= a.status g
FROM attempts a
CROSS JOIN (SELECT #g:=2, #r:=0) t1
WHERE a.`logindate` BETWEEN '2014-08-28 10:00:00' AND '2014-08-28 11:00:00'
ORDER BY id
) t
GROUP BY t.userid,t.consecutive,t.status
HAVING consecutive_count >= 3 AND t.status = 0;

MYSQL query - getting totals by month

http://sqlfiddle.com/#!2/6a6b1
The scheme is given above.. all I want to do is get the results as the total of sales/month... the user will enter a start date and end date and I can generate (in PHP) all the month and years for those dates. For example, if I want to know the total number of "sales" for 12 months, I know I can run 12 individual queries with start and end dates, but I want to run only one query where the result will look like:
Month numofsale
January - 2
Feb-1
March - 23
Apr - 10
and so on...
or just a list of sales without the months, I can then pair it to the array of months generated in the PHP ...any ideas...
Edit/schema and data pasted from sqlfiddle.com:
CREATE TABLE IF NOT EXISTS `lead_activity2` (
`lead_activity_id` int(11) NOT NULL AUTO_INCREMENT,
`sp_id` int(11) NOT NULL,
`act_date` datetime NOT NULL,
`act_name` varchar(255) NOT NULL,
PRIMARY KEY (`lead_activity_id`),
KEY `act_date` (`act_date`),
KEY `act_name` (`act_name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
INSERT INTO `lead_activity2` (`lead_activity_id`, `sp_id`, `act_date`, `act_name`) VALUES
(1, 5, '2012-10-16 16:05:29', 'sale'),
(2, 5, '2012-10-16 16:05:29', 'search'),
(3, 5, '2012-10-16 16:05:29', 'sale'),
(4, 5, '2012-10-17 16:05:29', 'DNC'),
(5, 5, '2012-10-17 16:05:29', 'sale'),
(6, 5, '2012-09-16 16:05:30', 'SCB'),
(7, 5, '2012-09-16 16:05:30', 'sale'),
(8, 5, '2012-08-16 16:05:30', 'sale'),
(9, 5,'2012-08-16 16:05:30', 'sale'),
(10, 5, '2012-07-16 16:05:30', 'sale');
SELECT DATE_FORMAT(date, "%m-%Y") AS Month, SUM(numofsale)
FROM <table_name>
WHERE <where-cond>
GROUP BY DATE_FORMAT(date, "%m-%Y")
Check following in your fiddle demo it works for me (remove where clause for testing)
SELECT DATE_FORMAT(act_date, "%m-%Y") AS Month, COUNT(*)
FROM lead_activity2
WHERE <where-cond-here> AND act_name='sale'
GROUP BY DATE_FORMAT(act_date, "%m-%Y")
It returns following result
MONTH COUNT(*)
07-2012 1
08-2012 2
09-2012 1
10-2012 3
You can try query as given below
select SUM(`SP_ID`) AS `Total` , DATE_FORMAT(act_date, "%M") AS Month, Month(`ACT_DATE`) AS `Month_number` from `lead_activity2` WHERE `ACT_DATE` BETWEEN '2012-05-01' AND '2012-12-17' group by Month(`ACT_DATE`)
Here 2012-05-01 and 2012-12-17 are date input from form. and It will be return you the sum of sales for particular month if exist in database.
thanks
Try this query -
SELECT
MONTH(act_date) month, COUNT(*)
FROM
lead_activity2
WHERE
YEAR(act_date) = 2012 AND act_name = 'sale'
GROUP BY
month
Check WHERE condition if it is OK for you - act_name = 'sale'.
If you want to output month names, then use MONTHNAME() function instead of MONTH().
SELECT YEAR(act_date), MONTH(act_date), COUNT(*)
FROM lead_activity2
GROUP BY YEAR(act_date), MONTH(act_date)
For getting data by month or any other data based on column you have to add GROUP BY.
You can add many columns or calculated values to GROUP BY.
I assume that "num of sales" means count of rows.
Sometimes you might want the month names as Jan, Feb, Mar .... Dec possibly for a Chart likeFusionChart
SELECT DATE_FORMAT(date, "%M") AS Month, SUM(numofsale)
FROM <Table_name>
GROUP BY DATE_FORMAT(date, "%M")
Results would look like this on table
MONTH COUNT(*)
Jul 1
Aug 2
SEP 1
OCT 3

Deleting rows based on date comparison - sql

I'm writing a SQL script to remove some rows from my table.
I need to remove the rows that have a certain type (easy), where the valuation timestamp is 16:00 on the current day when the create date is less than 4pm. Does that make sense?
delete from cfo_daily_trans_hist where dh_dd_type_id = 'valuation' --and dh_val_time_stamp is today at 16:00 where the dh_create_dt is today at a time earlier than 16:00
I just don't really know the syntax to do that exact date comparison.
DELETE [dbo].[cfo_daily_trans_hist]
WHERE [dh_dd_type_id] = 'valuation'
AND [dh_val_time_stamp] = DATEADD(HH, 16, CONVERT(DATETIME,CONVERT(DATE,GETDATE()))) -- is today at 16:00
AND [the dh_create_dt] < DATEADD(HH, 16, CONVERT(DATETIME,CONVERT(DATE,GETDATE())))-- is today at a time earlier than 16:00
And dh_val_time_stamp = DateAdd(Hour, 16, DateAdd(Day, DateDiff(Day, 0, GetDate()), 0))
And dh_create_dt >= DateAdd(Day, DateDiff(Day, 0, GetDate()), 0)
And dh_create_dt < DateAdd(Hour, 16, DateAdd(Day, DateDiff(Day, 0, GetDate()), 0))
-- =================
-- sample data
-- =================
declare #t table
(
dh_val_time_stamp datetime,
dh_create_dt datetime
)
declare #d datetime
set #d = cast(cast(cast(getdate() as float) as int) as datetime)
insert into #t values (dateadd(hh, 16, #d), dateadd(hh, 14, #d))
insert into #t values (dateadd(hh, 1, #d), dateadd(hh, 2, #d))
-- =================
-- solution
-- =================
select
*
from #t t1
cross apply
(
select d = dateadd(hh, 16, cast(cast(cast(getdate() as float) as int) as datetime))
) t2
where dh_val_time_stamp = d
and dh_create_dt < d