Using MySQL Pivot Table Queries for a Monthly Attendance Report - mysql

I have used pivot query for monthly report..
My table is like this:
CREATE TABLE IF NOT EXISTS `attendance` (
`empID` int(11) NOT NULL,
`date` date NOT NULL,
`deptID` int(11) NOT NULL,
`attStatus` varchar(3) NOT NULL,
PRIMARY KEY (`empID`,`date`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
For monthly attendance report, I have used this query:
SELECT empID,
GROUP_CONCAT(if(DAY(`date`) = 1, `attStatus`, NULL)) AS 'day1',
GROUP_CONCAT(if(DAY(`date`) = 2, `attStatus`, NULL)) AS 'day2',
GROUP_CONCAT(if(DAY(`date`) = 3, `attStatus`, NULL)) AS 'day3',
GROUP_CONCAT(if(DAY(`date`) = 4, `attStatus`, NULL)) AS 'day4',
GROUP_CONCAT(if(DAY(`date`) = 5, `attStatus`, NULL)) AS 'day5',
GROUP_CONCAT(if(DAY(`date`) = 6, `attStatus`, NULL)) AS 'day6',
GROUP_CONCAT(if(DAY(`date`) = 7, `attStatus`, NULL)) AS 'day7',
GROUP_CONCAT(if(DAY(`date`) = 8, `attStatus`, NULL)) AS 'day8',
GROUP_CONCAT(if(DAY(`date`) = 9, `attStatus`, NULL)) AS 'day9',
GROUP_CONCAT(if(DAY(`date`) = 10, `attStatus`, NULL)) AS 'day10',
GROUP_CONCAT(if(DAY(`date`) = 11, `attStatus`, NULL)) AS 'day11',
GROUP_CONCAT(if(DAY(`date`) = 12, `attStatus`, NULL)) AS 'day12',
GROUP_CONCAT(if(DAY(`date`) = 13, `attStatus`, NULL)) AS 'day13',
GROUP_CONCAT(if(DAY(`date`) = 14, `attStatus`, NULL)) AS 'day14',
GROUP_CONCAT(if(DAY(`date`) = 15, `attStatus`, NULL)) AS 'day15',
GROUP_CONCAT(if(DAY(`date`) = 16, `attStatus`, NULL)) AS 'day16',
GROUP_CONCAT(if(DAY(`date`) = 17, `attStatus`, NULL)) AS 'day17',
GROUP_CONCAT(if(DAY(`date`) = 18, `attStatus`, NULL)) AS 'day18',
GROUP_CONCAT(if(DAY(`date`) = 19, `attStatus`, NULL)) AS 'day19',
GROUP_CONCAT(if(DAY(`date`) = 20, `attStatus`, NULL)) AS 'day20',
GROUP_CONCAT(if(DAY(`date`) = 21, `attStatus`, NULL)) AS 'day21',
GROUP_CONCAT(if(DAY(`date`) = 22, `attStatus`, NULL)) AS 'day22',
GROUP_CONCAT(if(DAY(`date`) = 23, `attStatus`, NULL)) AS 'day23',
GROUP_CONCAT(if(DAY(`date`) = 24, `attStatus`, NULL)) AS 'day24',
GROUP_CONCAT(if(DAY(`date`) = 25, `attStatus`, NULL)) AS 'day25',
GROUP_CONCAT(if(DAY(`date`) = 26, `attStatus`, NULL)) AS 'day26',
GROUP_CONCAT(if(DAY(`date`) = 27, `attStatus`, NULL)) AS 'day27',
GROUP_CONCAT(if(DAY(`date`) = 28, `attStatus`, NULL)) AS 'day28',
GROUP_CONCAT(if(DAY(`date`) = 29, `attStatus`, NULL)) AS 'day29',
GROUP_CONCAT(if(DAY(`date`) = 30, `attStatus`, NULL)) AS 'day30',
GROUP_CONCAT(if(DAY(`date`) = 31, `attStatus`, NULL)) AS 'day31',
COUNT(if(`attStatus`='P', `attStatus`, NULL)) AS 'totalP'
FROM `attendance`
WHERE deptID=1 AND `date` BETWEEN '2011-07-01' AND '2011-07-31'
GROUP BY empID
So my question is, isn't this query too long?
Is there any alternative to this query to display monthly attendance report ?
If this query is okay then for those months which have 30 days in it, result for 31 days is displayed. Is there any solution to this?
UPDATE:
Here is a screenshot which explain what type of report I want:

No idea if your original query would work but something like this would be simpler:
select DAY(date),count(*) from attendance where MONTH(date) = 2 group by DAY(date);
That should get the attendance for February. The output would be:
+-----------+----------+
| DAY(date) | count(*) |
+-----------+----------+
| 18 | 1 |
| 19 | 2 |
+-----------+----------+

First, create a help table:
CREATE TABLE days
( d TINYINT PRIMARY KEY
) ;
INSERT INTO days
VALUES
(0), (1), (2), ..., (31) ;
Then you can try this. It will return less columns than your query though:
SELECT
a.empID
, GROUP_CONCAT( CASE WHEN a.attStatus IS NULL
THEN 'N'
ELSE a.attStatus
END
ORDER BY dates.dateCheck)
AS days
, COUNT( CASE WHEN a.attStatus='P' THEN 1 END )
AS totalP
FROM
( SELECT monthStart + INTERVAL d DAY
AS dateCheck
FROM days
CROSS JOIN
( SELECT '2011-07-01' AS monthStart
) AS m
WHERE MONTH(monthStart + INTERVAL d DAY) = MONTH(monthStart)
) AS dates
LEFT JOIN attendance AS a
ON a.`date` = dates.dateCheck
WHERE a.deptID = 1
GROUP BY a.empID

Related

Return the results of sql in only one row

I am getting all the possible members in users table using this query:
SELECT u.id as id ,
u.name as name,
u.age as age
FROM users u
Obviously the results is:
id name age
-----------------------
21 name1 age1
22 name2 age2
23 name3 age3
24 name4 age4
What I want is to get the final result below:
id_1 name_1 age_1 id_2 name_2 age_2 id_3 name_3 age_3 id_4 name_4 age_4
--------------------------------------------------------------------------------------------------------
21 name1 age1 22 name2 age2 23 name3 age3 24 name4 age4
As you can see in the final result I want to return it in one row. Is that possible? If its possible, Can I asked you any suggestion on how to achieve it? Any answers would be much appreciated.
The requirement doesn't look practical in data manipulation. But if it's what you're looking for as a mean for formatting...
SELECT
GROUP_CONCAT(if(id = 21, id, NULL)) AS id_1,
GROUP_CONCAT(if(id = 21, name, NULL)) AS name_1,
GROUP_CONCAT(if(id = 21, age, NULL)) AS age_1,
GROUP_CONCAT(if(id = 22, id, NULL)) AS id_2,
GROUP_CONCAT(if(id = 22, name, NULL)) AS name_2,
GROUP_CONCAT(if(id = 22, age, NULL)) AS age_2,
GROUP_CONCAT(if(id = 23, id, NULL)) AS id_3,
GROUP_CONCAT(if(id = 23, name, NULL)) AS name_3,
GROUP_CONCAT(if(id = 23, age, NULL)) AS age_3,
GROUP_CONCAT(if(id = 24, id, NULL)) AS id_4,
GROUP_CONCAT(if(id = 24, name, NULL)) AS name_4,
GROUP_CONCAT(if(id = 214, age, NULL)) AS age_5
FROM users
GROUP BY id

How to SUM TIMEDIFF's During hours

I hope this can clearly explain what I am looking for. I have searched read through a few articles on this site, but haven't found what I am looking for. I have also spent close to 3 hours trying to figure this out on my own.
I am trying to count the number of records and SUM the WorkTime. Here is my query I have been working with.
SELECT Log.User
, sum(if(hour(endtime) = 0, 1, 0)) AS Midnight
, sum(if(hour(endtime) = 1, 1, 0)) AS `1AM`
, sum(if(hour(endtime) = 2, 1, 0)) AS `2AM`
, sum(if(hour(endtime) = 3, 1, 0)) AS `3AM`
, sum(if(hour(endtime) = 4, 1, 0)) AS `4AM`
, sum(if(hour(endtime) = 5, 1, 0)) AS `5AM`
, sum(if(hour(endtime) = 6, 1, 0)) AS `6AM`
, sum(if(hour(endtime) = 7, 1, 0)) AS `7AM`
, sum(if(hour(endtime) = 8, 1, 0)) AS `8AM`
, sum(if(hour(endtime) = 9, 1, 0)) AS `9AM`
, sum(if(hour(endtime) = 10, 1, 0)) AS `10AM`
, sum(if(hour(endtime) = 11, 1, 0)) AS `11AM`
, sum(if(hour(endtime) = 12, 1, 0)) AS `12PM`
, sum(if(hour(endtime) = 13, 1, 0)) AS `1PM`
, sum(if(hour(endtime) = 14, 1, 0)) AS `2PM`
, sum(if(hour(endtime) = 15, 1, 0)) AS `3PM`
, sum(if(hour(endtime) = 16, 1, 0)) AS `4PM`
, sum(if(hour(endtime) = 17, 1, 0)) AS `5PM`
, sum(if(hour(endtime) = 18, 1, 0)) AS `6PM`
, sum(if(hour(endtime) = 19, 1, 0)) AS `7PM`
, sum(if(hour(endtime) = 20, 1, 0)) AS `8PM`
, if(hour(endtime) = 20, sec_to_time(sum(time_to_sec(endtime) - time_to_sec(starttime))), 0) AS `8PM Time`
, sum(if(hour(endtime) = 21, 1, 0)) AS `9PM`
, sum(if(hour(endtime) = 22, 1, 0)) AS `10PM`
, sum(if(hour(endtime) = 23, 1, 0)) AS `11PM`
FROM
(
SELECT user
, controlnumber
, starttime
, endtime
, timediff(endtime, starttime) AS Worktime
FROM
atrtaxcert.ordertimeworked
) AS Log
GROUP BY
Log.User;
These start and end times are only minutes apart.
Any guidance is much appreciated. This is my first post here, and was not able to provide any images to help describe.
If starttime and endtime are TIME datatypes, then use the TIME_TO_SEC function and do a subtraction. Total up the seconds, and then convert the total to a string representation.
SELECT `Log`.`User`
, ...
, SUM(HOUR(`Log`.endtime)=20) AS `8PM_count`
, SUM(IF(HOUR(`Log`.endtime)=20,work_seconds,0) AS `8PM_seconds`
, SEC_TO_TIME(SUM(IF(HOUR(`Log`.endtime)=20,`Log`.work_seconds,0) AS `8PM_hhhmmss`
, ...
FROM ( SELECT
, TIME_TO_SEC(endtime)-TIME_TO_SEC(starttime) AS work_seconds
) `Log`
GROUP
BY `Log`.`User`
NOTE: this:
SELECT HOUR(endtime)=0 AS foo
is shorthand equivalent to
SELECT IF(HOUR(endtime) = 0, 1, 0) AS foo
If starttime and endtime are DATETIME values, the you can use the TIMESTAMPDIFF function to calculate the difference in seconds:
SELECT `Log`.`User`
, ...
, SUM(HOUR(`Log`.endtime)=20) AS `8PM_count`
, SUM(IF(HOUR(endtime)=20,TIMESTAMPDIFF(SECOND,`Log`.starttime,`Log`.endtime),0) AS `8PM_seconds`
, ...
FROM (
) `Log`
GROUP
BY `Log`.`User`
(You probably want to ignore the values returned when e.g. starttime = '23:59:00' and endtime = '00:01:00', and that would require another conditional test.)

How to find data from last week in MySQL

I want to display data from
Q1: only last week of each student.
Q2: only last month of each student.
How can I achieve this?
DEMO for week
DEMO for month
CREATE TABLE `hw_homework` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`studentid` int(10) NOT NULL,
`subjectid` int(10) NOT NULL,
`assignment_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`teacherid` int(10) NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`id`)
) ;
INSERT INTO `hw_homework` (`id`, `studentid`, `subjectid`, `assignment_name`, `teacherid`,
`date`) VALUES
(1, 29, 5, '5E', 20, '2012-11-04 13:58:40'),
(2, 15, 5, '32B', 20, '2012-11-04 13:59:54'),
(3, 29, 4, 'Q2A', 20, '2012-10-30 17:53:46'),
(4, 29, 11, '6E', 20, '2012-11-02 20:06:39'),
(5, 29, 11, 'C15', 20, '2012-10-16 20:06:30'),
(6, 15, 11, '7A', 20, '2012-09-19 20:08:05'),
(7, 29, 5, '3B', 20, '2012-09-14 20:08:12'),
(8, 29, 13, '6E', 32, '2012-10-29 20:23:46'),
(9, 29, 11, '7E', 18, '2012-10-30 14:35:14'),
(10, 2, 5, '5E', 20, '2012-10-21 13:58:40'),
(11, 2, 5, '5E', 20, '2012-10-30 13:58:40'),
(12, 2, 5, '5E', 20, '2012-10-31 13:58:40');
This does not work for last week. It shows this week result.
SELECT studentID,
DATE_FORMAT(`date`, '%U') `WeekNo`,
COUNT(studentID) totalMissed
FROM hw_homework he
WHERE DATE_FORMAT(`date`, '%U') = (SELECT MAX(DATE_FORMAT(NOW(), '%U')) FROM hw_homework hi WHERE hi.studentID = he.studentID)
-- AND studentID = ''
GROUP BY studentID, DATE_FORMAT(`date`, '%U')
This does not work for last month. This shows this month result.
SELECT studentID,
DATE_FORMAT(`date`, '%M') `Month`,
COUNT(studentID) totalMissed
FROM hw_homework he
WHERE DATE_FORMAT(`date`, '%M') = (SELECT MAX(DATE_FORMAT(NOW(), '%M')) FROM hw_homework hi WHERE hi.studentID = he.studentID)
-- AND studentID = ''
GROUP BY studentID, DATE_FORMAT(`date`, '%M')
Thanks in advance.
try subtracting 1 from weekNo:
SELECT studentID,
DATE_FORMAT(`date`, '%U') `WeekNo`,
COUNT(studentID) totalMissed
FROM hw_homework he
WHERE DATE_FORMAT(`date`, '%U') =
(SELECT MAX(DATE_FORMAT(NOW(), '%U')-1)
FROM hw_homework hi
WHERE hi.studentID = he.studentID)
GROUP BY studentID, DATE_FORMAT(`date`, '%U')

MySql query and GROUP BY, what do I need to do differently?

Right now I have an sql query that selects a range of of my sales reps, then joins a customer table, and counts if certain values are true. My query works, however, It leaves out sales reps who have not sold any customers. I would like this query to return all sales reps, even if they have not created any client records matching the criteria. What do I need to change?
Here is a link to my SQL Query: http://pastie.org/4557540 (same as below)
SELECT u.id AS
`employee_id`,
u.`first_name` AS
`employee_first_name`,
u.`last_name` AS
`employee_last_name`,
(SELECT Count(*)
FROM saleset s
WHERE s.pitchedby_id = `employee_id`
AND s.pitchstartedat BETWEEN '2012-08-20 00:00:00' AND
'2012-08-20 23:59:59') AS
`transfers_taken`,
Count(IF(c.`saletype_id` IS NOT NULL, 1, NULL)) AS
`total_closes`,
Count(IF(c.`saletype_id` = 1, 1, NULL)) AS
`regular_sale`,
Count(IF(c.`saletype_id` = 2, 1, NULL)) AS
`postdated_sale`,
Count(IF(c.`saletype_id` = 4, 1, NULL)) AS
`attempted_sale`,
Count(IF(c.`customerstatus_id` IN ( 8, 18, 23 ), 1, NULL)) AS
`cancel_status`,
Count(IF(c.`customerstatus_id` IN ( 1, 12, 13, 24 ), 1, NULL)) AS
`pending_completion_status`,
Count(IF(c.`customerstatus_id` IN ( 5, 6, 16 ), 1, NULL)) AS
`complete_status`,
Count(IF(c.`customerstatus_id` = 20, 1, NULL)) AS
`postdate_pending`,
Count(IF(c.`customerstatus_id` = 25, 1, NULL)) AS
`postdate_declined`
FROM `user` u
LEFT JOIN customer c
ON c.`salesrep_id` = u.id
WHERE u.id IN ( 39, 65, 76, 96,
195, 266, 349, 401,
402, 404, 405, 407,
411, 412 )
AND c.`activationdate` BETWEEN
'2012-08-20 00:00:00' AND '2012-08-20 23:59:59'
GROUP BY u.`id`
The problem is that your are filtering the customers in the WHERE clause, after the LEFT JOIN is specified, so salesman with no sales get left out, because c.activationdate is null for these records.
The solution is having the client filtering inside the join conditions:
(...)
FROM `user` u
LEFT JOIN customer c
ON (c.`salesrep_id` = u.id and
u.id IN ( 39, 65, 76, 96, 195, 266, 349, 401,
402, 404, 405, 407,
411, 412 )
AND c.`activationdate` BETWEEN
'2012-08-20 00:00:00' AND '2012-08-20 23:59:59')
GROUP BY u.`id`
Perhaps you could take what you have an union it with your sales reps that have no sales, and just hard code them to 0. Something like:
SELECT u.id AS
`employee_id`,
u.`first_name` AS
`employee_first_name`,
u.`last_name` AS
`employee_last_name`,
(SELECT Count(*)
FROM saleset s
WHERE s.pitchedby_id = `employee_id`
AND s.pitchstartedat BETWEEN '2012-08-20 00:00:00' AND
'2012-08-20 23:59:59') AS
`transfers_taken`,
Count(IF(c.`saletype_id` IS NOT NULL, 1, NULL)) AS
`total_closes`,
Count(IF(c.`saletype_id` = 1, 1, NULL)) AS
`regular_sale`,
Count(IF(c.`saletype_id` = 2, 1, NULL)) AS
`postdated_sale`,
Count(IF(c.`saletype_id` = 4, 1, NULL)) AS
`attempted_sale`,
Count(IF(c.`customerstatus_id` IN ( 8, 18, 23 ), 1, NULL)) AS
`cancel_status`,
Count(IF(c.`customerstatus_id` IN ( 1, 12, 13, 24 ), 1, NULL)) AS
`pending_completion_status`,
Count(IF(c.`customerstatus_id` IN ( 5, 6, 16 ), 1, NULL)) AS
`complete_status`,
Count(IF(c.`customerstatus_id` = 20, 1, NULL)) AS
`postdate_pending`,
Count(IF(c.`customerstatus_id` = 25, 1, NULL)) AS
`postdate_declined`
FROM `user` u
INNER JOIN customer c
ON c.`salesrep_id` = u.id
WHERE u.id IN ( 39, 65, 76, 96,
195, 266, 349, 401,
402, 404, 405, 407,
411, 412 )
AND c.`activationdate` BETWEEN
'2012-08-20 00:00:00' AND '2012-08-20 23:59:59'
GROUP BY u.`id`
UNION
SELECT u.id AS
`employee_id`,
u.`first_name` AS
`employee_first_name`,
u.`last_name` AS
`employee_last_name`,
0 AS
`transfers_taken`,
Count(IF(c.`saletype_id` IS NOT NULL, 1, NULL)) AS
`total_closes`,
Count(IF(c.`saletype_id` = 1, 1, NULL)) AS
`regular_sale`,
Count(IF(c.`saletype_id` = 2, 1, NULL)) AS
`postdated_sale`,
Count(IF(c.`saletype_id` = 4, 1, NULL)) AS
`attempted_sale`,
Count(IF(c.`customerstatus_id` IN ( 8, 18, 23 ), 1, NULL)) AS
`cancel_status`,
Count(IF(c.`customerstatus_id` IN ( 1, 12, 13, 24 ), 1, NULL)) AS
`pending_completion_status`,
Count(IF(c.`customerstatus_id` IN ( 5, 6, 16 ), 1, NULL)) AS
`complete_status`,
Count(IF(c.`customerstatus_id` = 20, 1, NULL)) AS
`postdate_pending`,
Count(IF(c.`customerstatus_id` = 25, 1, NULL)) AS
`postdate_declined`
FROM `user` u
LEFT JOIN customer c
ON c.`salesrep_id` = u.id
WHERE u.id IN ( 39, 65, 76, 96,
195, 266, 349, 401,
402, 404, 405, 407,
411, 412 )
AND c.`activationdate` BETWEEN
'2012-08-20 00:00:00' AND '2012-08-20 23:59:59'
AND c.`salesrep_id` IS NULL
GROUP BY u.`id`

Reference not supported on group function

I have some problem using this query, it appears that i cannot use alias as reference on group by query
SELECT v.V_VEHICLEID, v.V_LICENSENO,
ROUND((MAX(IF(DAY(WP_DATETIME) = 1, WP_ODOMETER, NULL)) - MIN(IF(DAY(WP_DATETIME) = 1, WP_ODOMETER, NULL))) / 1000) AS TRIP1,
...,
ROUND((MAX(IF(DAY(WP_DATETIME) = 31, WP_ODOMETER, NULL)) - MIN(IF(DAY(WP_DATETIME) = 31, WP_ODOMETER, NULL))) / 1000) AS TRIP31,
ROUND(MAX(IF(DAY(WP_DATETIME) = 1, WP_FUELREAD, NULL)) - MIN(IF(DAY(WP_DATETIME) = 1, WP_FUELREAD, NULL)),2) AS FUEL1,
...,
ROUND(MAX(IF(DAY(WP_DATETIME) = 31, WP_FUELREAD, NULL)) - MIN(IF(DAY(WP_DATETIME) = 31, WP_FUELREAD, NULL)),2) AS FUEL31,
(SELECT TRIP1 / FUEL1) AS ECON1,
...,
(SELECT TRIP31 / FUEL31) AS ECON31
FROM VEHICLES v
JOIN WAYPOINTS wp on wp.WP_VEHICLEID = v.V_VEHICLEID
WHERE MONTH(wp.WP_DATETIME) = '6' AND v.V_USER = 'tc'
GROUP BY wp.WP_VEHICLEID
the error
Reference 'TRIP1' not supported (reference to group function)
any solution?
You can't use the column aliases you defined in a query in the same query. Try something like the following:
SELECT *,
TRIP1 / FUEL1 AS ECON1,
TRIP31 / FUEL31 AS ECON31,
OTHER COLUMNS....
FROM
(
SELECT v.V_VEHICLEID,
v.V_LICENSENO,
ROUND((MAX(IF(DAY(WP_DATETIME) = 1, WP_ODOMETER, NULL)) - MIN(IF(DAY(WP_DATETIME) = 1, WP_ODOMETER, NULL))) / 1000) AS TRIP1,
ROUND(MAX(IF(DAY(WP_DATETIME) = 1, WP_FUELREAD, NULL)) - MIN(IF(DAY(WP_DATETIME) = 1, WP_FUELREAD, NULL)),2) AS FUEL1,
ROUND((MAX(IF(DAY(WP_DATETIME) = 31, WP_ODOMETER, NULL)) - MIN(IF(DAY(WP_DATETIME) = 31, WP_ODOMETER, NULL))) / 1000) AS TRIP31,
ROUND(MAX(IF(DAY(WP_DATETIME) = 31, WP_FUELREAD, NULL)) - MIN(IF(DAY(WP_DATETIME) = 31, WP_FUELREAD, NULL)),2) AS FUEL31,
OTHER COLUMNS...
FROM VEHICLES v
JOIN WAYPOINTS wp
ON wp.WP_VEHICLEID = v.V_VEHICLEID
WHERE MONTH(wp.WP_DATETIME) = '6'
AND v.V_USER = 'tc'
GROUP BY wp.WP_VEHICLEID
) t
you did not specify the alias WP. take a look, it must be like this:
...
JOIN WAYPOINTS wp on wp.WP_VEHICLEID = v.V_VEHICLEID