How to add time in sql server 2008? - sql-server-2008

I want to calculate total hrs spend by an employee between 09:00am and 18:00pm.
My database look like this.
How can I do this??
AttendanceId EmpId CheckTime CheckType
-------------------------------------------------------------------------
3 5 2013-01-03 09:00:15.000 1 (Login)
4 5 2013-01-03 11:00:00.000 2 (Logout)
5 5 2013-01-03 11:30:00.000 1
6 5 2013-01-03 13:00:00.000 2
7 5 2013-01-03 13:30:00.000 1
8 5 2013-01-03 16:00:00.000 2
9 5 2013-01-03 16:30:00.000 1
10 5 2013-01-03 18:00:00.000 2

Since your Login/Logout values are in the same column, this might be easier to PIVOT the login/logout times first then get the datediff to determine the total amount of time an employee is present.
The PIVOT portion of the query is this:
select empid, [1], [2]
from
(
select empid, checktime, checktype,
row_number() over(partition by empid, checktype order by checktime) rn
from yourtable
) src
pivot
(
max(checktime)
for checktype in ([1], [2])
) piv
See SQL Fiddle with Demo
The result of this is:
| EMPID | 1 | 2 |
---------------------------------------------------------------------------
| 5 | January, 03 2013 09:00:15+0000 | January, 03 2013 11:00:00+0000 |
| 5 | January, 03 2013 11:30:00+0000 | January, 03 2013 13:00:00+0000 |
| 5 | January, 03 2013 13:30:00+0000 | January, 03 2013 16:00:00+0000 |
| 5 | January, 03 2013 16:30:00+0000 | January, 03 2013 18:00:00+0000 |
Once the data is in this structure, you can easily get the difference in the time by applying the DateDiff() function.
The final query to generate the amount of time an employee is logged in is:
select empid, sum(SecondsDiff) / 3600 as TotalHours
from
(
select empid, datediff(ss, [1], [2]) SecondsDiff
from
(
select empid, checktime, checktype,
row_number() over(partition by empid, checktype order by checktime) rn
from yourtable
) src
pivot
(
max(checktime)
for checktype in ([1], [2])
) piv
) src
group by empid
See SQL Fiddle with Demo
And the result is:
| EMPID | TOTALHOURS |
----------------------
| 5 | 7 |

To get the difference between two dates, you use the DATEDIFF function:
http://msdn.microsoft.com/en-us/library/ms189794.aspx
I think you'd need to do this row-by-row, though. Because of the structure of your table, you can't just do a simple query.

Related

How to write an SQL query to calculate the average for three consecutive values?

I have a table like this
userID time NoOfVisits
1 2014 50
2 2015 60
3 2016 70
4 2017 80
5 2018 90
6 2019 100
I need to write a sql query which will print time and average of past 3 years NoOfVisits for a particular site.
output should be as
userID time NoOfVisits
1 2014 50.0000
2 2015 55.0000
3 2016 60.0000
4 2017 70.0000
5 2018 80.0000
6 2019 90.0000
Explanation :
For user Id 6 (80+90+100)/3=90.0000
Please help me to solve this problem.
You can use a cumulative average, available in MySQL 8+:
select t.*,
avg(visits) over (order by time rows between 2 preceding and current row) as avg_visits_3
from t;
Assuming there are no gaps between the years (like your sample data), you can self join the table and group by userid, time to get the average:
select
t.userid, t.time, avg(tt.noofvisits) NoOfVisits
from tablename t inner join tablename tt
on tt.time between t.time - 2 and t.time
group by t.userid, t.time
See the demo.
Results:
| userid | time | NoOfVisits |
| ------ | ---- | ---------- |
| 1 | 2014 | 50 |
| 2 | 2015 | 55 |
| 3 | 2016 | 60 |
| 4 | 2017 | 70 |
| 5 | 2018 | 80 |
| 6 | 2019 | 90 |

mysql complex query for monthly report

employee makes entry in the following table when starting new task
from home or office
[tablename=CHECK]
c_id c_sdate c_emp c_task
-------------------------------------------------
1 2013-05-01 01:01:00 1 26 //date 01 from home-----
2 2013-05-01 08:11:00 1 27 //date 01 from office--- Present
3 2013-05-02 03:41:00 1 28 //date 02 from home---
4 2013-05-02 09:12:00 1 29 //date 02 from office-
5 2013-05-02 22:32:00 1 30 //date 02 from home---Present
6 2013-05-03 01:43:00 1 31 //date 03 from home
7 2013-06-03 23:25:00 1 32 //date 03 from home----------Homework
8 2013-06-03 02:15:00 2 33 //other employee
an employe will be considered as present if there 1 or many records where time between 8am and 8pm
an employe will be considered as workedFromHome if there 1 or many records where time NOT between 8am and 8pm, and not present on that day
note: do not count a day as workedFromHome if there is any record time between 8am and 8pm (means workedFromHome is only counted if he is not resent on that day)
I want to display monthly report of a employee eg. c_emp=1 for month eg. 5
like this in 1 query
c_emp presentCount HW_Count
1 3 1
or separatly query 1
c_emp presentCount
1 3
and query 2
c_emp HW_Count
1 1
I have tried for counting present working fine
select count(distinct(date_format(c_sdate,'%e'))) as count
from ita_check
where date_format(c_sdate,'%m')=5
and c_emp=1
and date_format(c_sdate,'%H%i')>=800
and date_format(c_sdate,'%H%i')<=2000
and for counting fromHome giving wrong count
select count(distinct(date_format(c_sdate,'%e'))) as count
from ita_check
where date_format(c_sdate,'%m')=5
and c_eid=1
and c_id not in (
select c_id
from ita_check
where date_format(c_sdate,'%m')=5
and c_eid=1
and (date_format(c_sdate,'%H%i')<=800 or date_format(c_sdate,'%H%i')>=2000)
)
and date_format(c_sdate,'%H%i')<800
or date_format(c_sdate,'%H%i')>2000
in above query for counting Working
the sub query returns 1 and 2
while the outer eliminate c_id=2 but not c_id=1
Try this query
SELECT c_emp,
sum(if(cnt>=1,1,0)) as Office,
count(*)-sum(if(cnt>=1,1,0)) as WFH from (
select c_emp, Date(c_sdate),
sum(if(c_sdate BETWEEN Date(c_sdate) + interval 8 hour
AND Date(c_sdate) + interval 20 hour, 1, 0)) as cnt
from table1
group by c_emp, Date(c_sdate)) tmp
group by c_emp
SQL FIDDLE:
| C_EMP | OFFICE | WFH |
------------------------
| 1 | 2 | 2 |
| 2 | 0 | 1 |
For monthly report
SELECT c_emp, date_format(c_date, '%c %Y') as Mnth,
sum(if(cnt>=1,1,0)) as Office,
count(*)-sum(if(cnt>=1,1,0)) as WFH from (
select c_emp, Date(c_sdate) as c_date,
sum(if(c_sdate BETWEEN Date(c_sdate) + interval 8 hour
AND Date(c_sdate) + interval 20 hour, 1, 0)) as cnt
from table1
group by c_emp, Date(c_sdate)) tmp
group by c_emp,Mnth
SQL FIDDLE:
| C_EMP | MNTH | OFFICE | WFH |
---------------------------------
| 1 | 5 2013 | 2 | 1 |
| 1 | 6 2013 | 0 | 1 |
| 2 | 6 2013 | 0 | 1 |

Group dates by only month and year

Assuming I have a table like the following:
id | assignment | duedate
1 | Math | 2012-01-01
2 | History | 2012-02-02
3 | Science | 2012-01-01
4 | Government | 2012-02-01
5 | Government | 2013-01-13
6 | History | 2013-03-13
Is it possible to make some sql query such that I get a grouping of all the dates by month and year? Is there some possibility that I could get a sorted result of:
duedatemonth | count
January 2012 | 2
Feburary 2012 | 2
January 2013 | 1
March 2013 | 1
I know you can GROUP BY duedate, but that only groups those with the same month, day, and year instead of just month and year.
Would it be then possible to even further group it such that it factors in "assignment" to obtain a resulting table of
id | duedatemonth | count
1 | January 2012 | 2
3 | January 2012 | 2
2 | Feburary 2012 | 2
4 | Feburary 2012 | 2
5 | January 2013 | 1
6 | March 2013 | 1
try this
SELECT DATE_FORMAT(duedate,'%M %Y') duedatemonth, COUNT(*) count
FROM Table1
GROUP BY year(duedate), MONTH(duedate)
DEMO HERE
will output this:
DUEDATEMONTH COUNT
January 2012 2
February 2012 2
January 2013 1
March 2013 1
Use the string functions YEAR and MONTH.
SELECT YEAR(duedate), MONTH(duedate), COUNT(*)
FROM sparkles
GROUP BY YEAR(duedate), MONTH(duedate)
Use MONTHNAME or DATE_FORMAT to get the name of the month.
You can use this query.
SELECT DATE_FORMAT(duedate, '%M %Y') duedatemonth, COUNT(1) `count`
FROM Tbl
GROUP BY DATE_FORMAT(duedate, '%M %Y')

Select highest 3 scores in each day for every user

I have a MYSQL table like this:
id | userid | score | datestamp |
-----------------------------------------------------
1 | 1 | 5 | 2012-12-06 03:55:16
2 | 2 | 0,5 | 2012-12-06 04:25:21
3 | 1 | 7 | 2012-12-06 04:35:33
4 | 3 | 12 | 2012-12-06 04:55:45
5 | 2 | 22 | 2012-12-06 05:25:11
6 | 1 | 16,5 | 2012-12-06 05:55:21
7 | 1 | 19 | 2012-12-06 13:55:16
8 | 2 | 8,5 | 2012-12-07 06:27:16
9 | 2 | 7,5 | 2012-12-07 08:33:16
10 | 1 | 10 | 2012-12-07 09:25:19
11 | 1 | 6,5 | 2012-12-07 13:33:16
12 | 3 | 6 | 2012-12-07 15:45:44
13 | 2 | 4 | 2012-12-07 16:05:16
14 | 2 | 34 | 2012-12-07 18:33:55
15 | 2 | 22 | 2012-12-07 18:42:11
I would like to display user scores like this:
if a user on a certain day has more than 3 scores it would get only highest 3, repeat that for every day for this user and then add all days together. I want to display this sum for every user.
EDIT:
So in the example above for user 1 on 06.12. I would add top 3 scores together and ignore 4th score, then add to that number top 3 from the next day and so on. I need that number for every user.
EDIT 2:
Expected output is:
userid | score
--------------------
1 | 59 //19 + 16.5 + 7 (06.12.) + 10 + 6.5 (07.12.)
2 | 87 //22 + 0.5 (06.12.) + 34 + 22 + 8.5 (07.12.)
3 | 18 //12 (06.12.) + 6 (07.12.)
I hope this is more clear :)
I would really appreciate the help because I am stuck.
Please take a look at the following code, if your answer to my comment is yes :) Since your data all in 2012, and month of november, I took day.
SQLFIDDLE sample
Query:
select y.id, y.userid, y.score, y.datestamp
from (select id, userid, score, datestamp
from scores
group by day(datestamp)) as y
where (select count(*)
from (select id, userid, score, datestamp
from scores group by day(datestamp)) as x
where y.score >= x.score
and y.userid = x.userid
) =1 -- Top 3rd, 2nd, 1st
order by y.score desc
;
Results:
ID USERID SCORE DATESTAMP
8 2 8.5 December, 07 2012 00:00:00+0000
20 3 6 December, 08 2012 00:00:00+0000
1 1 5 December, 06 2012 00:00:00+0000
Based on your latter updates to question.
If you need some per user by year/month/day and then find highest, you may simply add aggregation function like sum to the above query. I am reapeating myself, since your sample data is for just one year, there's no point group by year or month. That's why I took day.
select y.id, y.userid, y.score, y.datestamp
from (select id, userid, sum(score) as score,
datestamp
from scores
group by userid, day(datestamp)) as y
where (select count(*)
from (select id, userid, sum(score) as score
, datestamp
from scores
group by userid, day(datestamp)) as x
where y.score >= x.score
and y.userid = x.userid
) =1 -- Top 3rd, 2nd, 1st
order by y.score desc
;
Results based on sum:
ID USERID SCORE DATESTAMP
1 1 47.5 December, 06 2012 00:00:00+0000
8 2 16 December, 07 2012 00:00:00+0000
20 3 6 December, 08 2012 00:00:00+0000
UPDATED WITH NEW SOURCE DATA SAMPLE
Simon, please take a look at my own sample. As your data was changing, I used mine.
Here is the reference. I have used pure ansi style without any over partition or dense_rank.
Also note the data I used are getting top 2 not top 3 scores. You can change is accordingly.
Guess what, the answer is 10 times simpler than the first impression your first data gave....
SQLFIDDLE
Query to 1:
-- for top 2 sum by user by each day
SELECT userid, sum(Score), datestamp
FROM scores t1
where 2 >=
(SELECT count(*)
from scores t2
where t1.score <= t2.score
and t1.userid = t2.userid
and day(t1.datestamp) = day(t2.datestamp)
order by t2.score desc)
group by userid, datestamp
;
Results for query 1:
USERID SUM(SCORE) DATESTAMP
1 70 December, 06 2012 00:00:00+0000
1 30 December, 07 2012 00:00:00+0000
2 22 December, 06 2012 00:00:00+0000
2 25 December, 07 2012 00:00:00+0000
3 30 December, 06 2012 00:00:00+0000
3 30 December, 07 2012 00:00:00+0000
Final Query:
-- for all two days top 2 sum by user
SELECT userid, sum(Score)
FROM scores t1
where 2 >=
(SELECT count(*)
from scores t2
where t1.score <= t2.score
and t1.userid = t2.userid
and day(t1.datestamp) = day(t2.datestamp)
order by t2.score desc)
group by userid
;
Final Results:
USERID SUM(SCORE)
1 100
2 47
3 60
Here goes a snapshot of direct calculations of data I used.
SELECT
*
FROM
table1
LEFT JOIN
(SELECT * FROM table1 ORDER BY score LIMIT 3) as lr on DATE(lr.datestamp) = DATE(table1.datastamp)
GROUP BY
datestamp

MySql subquerying to pull data from another table

I am currently using phpMyAdmin supporting MySql 5.0.
I have the following two tables:
Master Facebook Insights
------------------------
Client ID Client Name Date Top Likes By Gender
--------- ----------- ------ --------------------
1 Client1 2012-01-01
1 Client1 2012-02-01
2 Client2 2012-01-01
2 Client2 2012-02-01
...etc. (the dates are always the beginning of each month for however many months
& clients exist)
Likes By Demographic
----------------
Date Demographic1 Demographic2 Demographic3 Client
------ ------------ ------------ ------------ ------
0000-00-00 M.35-44 M.45-54 M.25-34 1
2012-01-01 53 44 28 1
2012-01-02 53 46 29 1
...etc.
0000-00-00 M.18-24 M.35-44 M.25-34 1
2012-02-01 374 221 194 1
2012-02-02 374 222 195 1
...etc.
0000-00-00 F.35-44 F.25-34 M.35-44 2
2012-01-01 194 182 83 2
2012-01-02 194 182 83 2
...etc.
0000-00-00 F.35-44 F.25-34 M.35-44 2
2012-02-01 196 180 83 2
2012-02-02 197 180 83 2
...etc.
For the Likes By Demographic table:
All days of each month are listed per client; when a new month begins,
there is a new set of demographics that may or may NOT be listed exactly the same
as the previous month. This is because the data was imported from a CSV that put the demographics in order of highest 'likes,' so this usually changes month to month. This is also the reason that the individual demographics are not the column headers (because they are inconsistent).
My problem is the following:
I wish to list the top 3 demographics per month for each client in the 'Top Likes By Gender' column of the first table, like so:
Client ID Client Name Date Top Likes By Gender
--------- ----------- ------ --------------------
1 Client1 2012-01-01 M.35-44, M.45-54, M.25-34
1 Client1 2012-02-01 M.18-24, M.35-44, M.25-34
2 Client2 2012-01-01 F.35-44, F.25-34, M.35-44
2 Client2 2012-02-01 F.35-44, F.25-34, M.35-44
The use of subqueries is confusing me. The following is the (incorrect) code I have been trying to fix. The problem is that is just extracts the first three demographics for the first client (M.35-44, M.45-54, M.25-34) and repeats them down the entire column for all clients and dates.
Am I on the right track, or is there a much simpler/more correct way to do this?
UPDATE `Master Facebook Insights`
SET `Top Likes By Gender` =
(select * from
(SELECT DISTINCT CONCAT(`Demographic1`,', ',`Demographic2`,', ',`Demographic3`)
FROM `Likes By Demographic` t1
JOIN `Master Facebook Insights` t2
ON t1.`Client` = t2.`Client ID`
WHERE t1.`Date` = '0000-00-00'
AND t2.`Date` =
(
SELECT MIN(`Date`)
FROM `Likes By Demographic`
WHERE `Date` > '0000-00-00'
AND `Client` = t2.`Client ID`
GROUP BY `Date`
ORDER BY `Date` ASC
LIMIT 1
)
)inner1 limit 1)
Thank you so much for the help!
Just using the original Likes_By_Demographic table, you can do:
select
client_id,
date,
concat(demo1,', ',demo2,', ',demo3) `top_likes`
from likes_by_demographic
group by client_id, date
order by client_id, date;
...which will yield (pseudo-data):
| CLIENT_ID | DATE | TOP_LIKES |
-----------------------------------------------------------------------
| 1 | January, 01 2012 00:00:00-0800 | M.35-44, M.25-34, 195 |
| 1 | February, 01 2012 00:00:00-0800 | M.25-34, M.35-44, 195 |
| 1 | March, 01 2012 00:00:00-0800 | M.25-34, 195, 169 |
| 2 | January, 01 2012 00:00:00-0800 | F.45-54, 210, 195 |
| 2 | February, 01 2012 00:00:00-0800 | F.45-54, 210, 75 |