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

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`

Related

How to get open and close time slots in MYSQL

I have sample data like this :
ID Val Name Dt Status
1, 145, 'Test1', '2020-01-28 02:18:00', 'open'
2, 145, 'Test1', '2020-01-28 04:10:00', 'open'
3, 145, 'Test1', '2020-01-28 05:50:00', 'open'
4, 145, 'Test1', '2020-01-28 05:56:00', 'close'
5, 145, 'Test2', '2020-01-28 07:36:00', 'open'
6, 145, 'Test2', '2020-01-28 07:42:00', 'open'
7, 145, 'Test2', '2020-01-28 07:44:00', 'open'
8, 145, 'Test2', '2020-01-28 07:47:00', 'close'
How can i get the output like this :
ID Val Name o_Dt o_gate c_Dt c_gate
1, 145, 'Test1', '2020-01-28 02:18:00', 'open' NULL NULL
2, 145, 'Test1', '2020-01-28 04:10:00', 'open' NULL NULL
3, 145, 'Test1', '2020-01-28 05:50:00', 'open' '2020-01-28 05:56:00', 'close'
4, 145, 'Test2', '2020-01-28 07:36:00', 'open' NULL NULL
5, 145, 'Test2', '2020-01-28 07:42:00', 'open' NULL NULL
6, 145, 'Test2', '2020-01-28 07:44:00', 'open' '2020-01-28 07:47:00', 'close'
I Have tried with different scenarios but not moving forward Using
COALESCE(LAG(Status) OVER (ORDER BY dt)
ROW_NUMBER()OVER(PARTITION BY vehicle_id,status )
Not getting exact result . Can anyone suggest on this .
Previously I have asked question for same data but haven't got exact answer .
How to transpose rows to columns based on time intervals in MYSQL
SELECT t1.ID, t1.Val, t1.Name, t1.Dt o_Dt, t1.Status o_gate, t2.Dt c_Dt, t2.Status c_gate
FROM test t1
LEFT JOIN test t2 ON t1.Dt < t2.Dt
AND t1.Name = t2.Name
AND t2.Status = 'close'
AND NOT EXISTS ( SELECT NULL
FROM test t3
WHERE t1.Dt < t3.Dt
AND t3.Dt < t2.Dt
AND t1.Name = t3.Name)
WHERE t1.Status = 'open'
fiddle
Try this Below Code
;WITH CTE(ID ,Val,Name,Dt,[Status])
AS
(
SELECT 1, 145, 'Test1', '2020-01-28 02:18:00', 'open'
UNION ALL SELECT 2, 145, 'Test1', '2020-01-28 04:10:00', 'open'
UNION ALL SELECT 3, 145, 'Test1', '2020-01-28 05:50:00', 'open'
UNION ALL SELECT 4, 145, 'Test1', '2020-01-28 05:56:00', 'close'
UNION ALL SELECT 5, 145, 'Test2', '2020-01-28 07:36:00', 'open'
UNION ALL SELECT 6, 145, 'Test2', '2020-01-28 07:42:00', 'open'
UNION ALL SELECT 7, 145, 'Test2', '2020-01-28 07:44:00', 'open'
UNION ALL SELECT 8, 145, 'Test2', '2020-01-28 07:47:00', 'close'
UNION ALL SELECT 9, 145, 'Test3', '2020-01-28 11:42:00', 'open'
UNION ALL SELECT 10, 145, 'Test3', '2020-01-28 14:44:00', 'open'
UNION ALL SELECT 11, 145, 'Test3', '2020-01-28 15:56:00', 'close'
)
SELECT ROW_NUMBER()OVER(ORDER BY (SELECT NULL)) AS ID,*
FROM
(
SELECT Val,Name,[Ogate],[O_Dt],LEAD([C_Dt])OVER(PARTITION BY Name ORDER BY ID)AS [C_Dt] ,LEAD([C_gate])OVER(PARTITION BY Name ORDER BY ID) AS [C_gate]
FROM CTE c
CROSS APPLY (VALUES ( CASE WHEN [Status] = 'open' THEN [Status] END,
CASE WHEN [Status] = 'open' THEN [Dt] END,
CASE WHEN [Status] = 'close' THEN [Status] END,
CASE WHEN [Status] = 'close' THEN [Dt] END
)) AS Dt ([Ogate],[O_Dt],[C_gate],[C_Dt])
)Dt
WHERE COALESCE (Ogate,O_Dt,C_Dt,C_gate,'1') <> '1'
Result
ID Val Name Ogate O_Dt C_Dt C_gate
---------------------------------------------------------------------------
1 145 Test1 open 2020-01-28 02:18:00 NULL NULL
2 145 Test1 open 2020-01-28 04:10:00 NULL NULL
3 145 Test1 open 2020-01-28 05:50:00 2020-01-28 05:56:00 close
4 145 Test2 open 2020-01-28 07:36:00 NULL NULL
5 145 Test2 open 2020-01-28 07:42:00 NULL NULL
6 145 Test2 open 2020-01-28 07:44:00 2020-01-28 07:47:00 close
7 145 Test3 open 2020-01-28 11:42:00 NULL NULL
8 145 Test3 open 2020-01-28 14:44:00 2020-01-28 15:56:00 close

optimizing mysql query with multiple sum(if(condition))

I have written a query which consists of multiple joins and sum(if()) condition.
I am using MySql Database.
The query structure seems like this:
*The query joins from multiple tables(7 to be precise) and the sum(if()) conditions is to represent total 53 states of US.*It takes almost 20 minutes for the query to execute which is a very long period.
If anyone could suggest how can I reduce this large amount of time.
SELECT ag.position_id AS 'position_id',
ag.npn AS 'NPN',
concat(ag.first_name, ' ', ag.last_name) AS 'full_name',
lb.name AS 'lob',
location_lic AS 'loc',
COUNT(DISTINCT lt.id) AS 'RTS_Total',
SUM(IF(st.id = 1, 1, 0)) AS 'AK',
SUM(IF(st.id = 2, 1, 0)) AS 'AL',
SUM(IF(st.id = 3, 1, 0)) AS 'AR',
SUM(IF(st.id = 4, 1, 0)) AS 'AZ',
SUM(IF(st.id = 5, 1, 0)) AS 'CA',
SUM(IF(st.id = 6, 1, 0)) AS 'CO',
SUM(IF(st.id = 7, 1, 0)) AS 'CT',
SUM(IF(st.id = 8, 1, 0)) AS 'DC',
SUM(IF(st.id = 9, 1, 0)) AS 'DE',
SUM(IF(st.id = 10, 1, 0)) AS 'FL',
SUM(IF(st.id = 11, 1, 0)) AS 'GA',
SUM(IF(st.id = 12, 1, 0)) AS 'HI',
SUM(IF(st.id = 13, 1, 0)) AS 'IA',
SUM(IF(st.id = 14, 1, 0)) AS 'ID',
SUM(IF(st.id = 15, 1, 0)) AS 'IL',
SUM(IF(st.id = 16, 1, 0)) AS 'IN',
SUM(IF(st.id = 17, 1, 0)) AS 'KS',
SUM(IF(st.id = 18, 1, 0)) AS 'KY',
SUM(IF(st.id = 19, 1, 0)) AS 'LA',
SUM(IF(st.id = 20, 1, 0)) AS 'MA',
SUM(IF(st.id = 21, 1, 0)) AS 'MD',
SUM(IF(st.id = 22, 1, 0)) AS 'ME',
SUM(IF(st.id = 23, 1, 0)) AS 'MI',
SUM(IF(st.id = 24, 1, 0)) AS 'MN',
SUM(IF(st.id = 25, 1, 0)) AS 'MO',
SUM(IF(st.id = 26, 1, 0)) AS 'MS',
SUM(IF(st.id = 27, 1, 0)) AS 'MT',
SUM(IF(st.id = 28, 1, 0)) AS 'NC',
SUM(IF(st.id = 29, 1, 0)) AS 'ND',
SUM(IF(st.id = 30, 1, 0)) AS 'NE',
SUM(IF(st.id = 31, 1, 0)) AS 'NH',
SUM(IF(st.id = 32, 1, 0)) AS 'NJ',
SUM(IF(st.id = 33, 1, 0)) AS 'NM',
SUM(IF(st.id = 34, 1, 0)) AS 'NV',
SUM(IF(st.id = 35, 1, 0)) AS 'NY',
SUM(IF(st.id = 36, 1, 0)) AS 'OH',
SUM(IF(st.id = 37, 1, 0)) AS 'OK',
SUM(IF(st.id = 38, 1, 0)) AS 'OR',
SUM(IF(st.id = 39, 1, 0)) AS 'PA',
SUM(IF(st.id = 40, 1, 0)) AS 'PR',
SUM(IF(st.id = 41, 1, 0)) AS 'RI',
SUM(IF(st.id = 42, 1, 0)) AS 'SC',
SUM(IF(st.id = 43, 1, 0)) AS 'SD',
SUM(IF(st.id = 44, 1, 0)) AS 'TN',
SUM(IF(st.id = 45, 1, 0)) AS 'TX',
SUM(IF(st.id = 46, 1, 0)) AS 'UT',
SUM(IF(st.id = 47, 1, 0)) AS 'VA',
SUM(IF(st.id = 48, 1, 0)) AS 'VI',
SUM(IF(st.id = 49, 1, 0)) AS 'VT',
SUM(IF(st.id = 50, 1, 0)) AS 'WA',
SUM(IF(st.id = 51, 1, 0)) AS 'WI',
SUM(IF(st.id = 52, 1, 0)) AS 'WV',
SUM(IF(st.id = 53, 1, 0)) AS 'WY'
FROM cxprtsapp_licensetracker lt
INNER JOIN cxprtsapp_agents ag ON ag.id = lt.agent_id_id AND ag.position_status LIKE 'Active'
LEFT JOIN cxprtsapp_statelobjit slj ON slj.state_id = lt.state_id_id AND ag.lob_id = slj.lob_id
LEFT JOIN
(select npn, rtsreport_appointed, rtsreport_licensed, state_id, lob_id
from cxprtsapp_rtslob
order BY refresh_number desc
) rts ON rts.state_id = lt.state_id_id and ag.lob_id = rts.lob_id and ag.npn=rts.npn
INNER JOIN cxprtsapp_location lc ON lc.id = ag.loc_id
INNER JOIN cxprtsapp_lineofbussiness lb ON lb.id = ag.lob_id
INNER JOIN cxprtsapp_states st ON st.id = lt.state_id_id
LEFT JOIN
(SELECT DISTINCT npn, state_code, status
FROM cxprtsapp_pdbappointments
WHERE status LIKE 'Appointed'
) appt ON appt.npn = ag.npn AND appt.state_code = st.state_code
WHERE certification_id IS NOT NULL
AND (expiration_date IS NULL OR expiration_date > CURDATE())
AND (slj.jit = 1 OR rts.rtsreport_appointed = 1 OR appt.status IS NOT NULL)
GROUP BY ag.id, ag.lob_id,loc_id ORDER BY ag.hire_date DESC;
Solution:
The time taken for the query has reduced from 10-15 min to 10-20 sec.
We must alter database index for performance.
we must the run the below sql query in the database first.
‌
ALTER TABLE cxprtsapp_agents ADD INDEX cxprtsapp_agents_idx_status_id_id_npn_id (position_status,id,lob_id,npn,loc_id);
ALTER TABLE cxprtsapp_licensetracker ADD INDEX cxprtsapp_licensetra_idx_id_id_id_id (agent_id_id,state_id_id,certification_id,id);
ALTER TABLE cxprtsapp_lineofbussiness ADD INDEX cxprtsapp_lineofbuss_idx_id_name (id,name);
ALTER TABLE cxprtsapp_location ADD INDEX cxprtsapp_location_idx_id_lic (id,location_lic);
ALTER TABLE cxprtsapp_statelobjit ADD INDEX cxprtsapp_statelobji_idx_id_id (state_id,lob_id);
ALTER TABLE cxprtsapp_states ADD INDEX cxprtsapp_states_idx_id_code (id,state_code);
ALTER TABLE cxprtsapp_rtslob ADD INDEX cxprtsapp_rtslob_idx_numbe_npn_appoi_licen_id_id (refresh_number,npn,rtsreport_appointed,rtsreport_licensed,state_id,lob_id);
ALTER TABLE cxprtsapp_pdbappointments ADD INDEX cxprtsapp_pdbappoint_idx_status_npn_code (status,npn,state_code);

GROUP_CONCAT not working in Sub-Query

I have tried this query to get the following data
SELECT
GROUP_CONCAT( sb.p_id ) ,
TRUNCATE( SUM( sb.total_amount ) , 2 ) grand_total,
TRUNCATE( SUM( sb.amount_wot ) , 2 ) sale_amount,
(SELECT TRUNCATE( SUM( (item_qty * item_price) * tax_price /100 ) , 2 )
FROM ci_bill_items
WHERE bill_id IN ( GROUP_CONCAT( sb.p_id ) ) ) tax_amount
FROM ci_suppliers_bills sb
WHERE sb.p_id >0
I got the expected result but not the tax_amount its return null but if i run the seperate query like :
SELECT TRUNCATE( SUM( (item_qty * item_price) * tax_price /100 ) , 2 )
FROM ci_bill_items
WHERE bill_id IN ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 71 )
Then i get the correct result. But all i want in one query like the first one, is group_concat not working like i tried bill_id IN ( GROUP_CONCAT( sb.p_id ) )? Help is much appriciated.
You can use FIND_IN_SET like this:
SELECT
GROUP_CONCAT( sb.p_id ) ,
TRUNCATE( SUM( sb.total_amount ) , 2 ) grand_total,
TRUNCATE( SUM( sb.amount_wot ) , 2 ) sale_amount,
(SELECT TRUNCATE( SUM( (item_qty * item_price) * tax_price /100 ) , 2 )
FROM ci_bill_items
WHERE FIND_IN_SET(bill_id, GROUP_CONCAT( sb.p_id ) ) > 0 ) tax_amount
FROM ci_suppliers_bills sb
WHERE sb.p_id >0
GROUP_CONCAT doesn't return a list, it returns a string. To get a list that you can use with IN, you need to run a subquery:
WHERE bill_id IN (SELECT p_id FROM ci_suppliers_bills
WHERE p_id > 0)

MySQL include zero rows when using COUNT with LEFT OUTER JOIN and GROUP BY

How can I avoid eliminating the users with zero meetings? I'm aware there are similar questions, but this code is quite a bit more complex.
SELECT user.userID, user.contactName, user.email, COUNT( * ) AS meetingsCount
FROM user
LEFT OUTER JOIN meeting ON user.userID = meeting.userID
WHERE user.userID NOT
IN ( 1, 2, 3, 4, 5, 59, 62, 63, 64, 66, 69, 71, 72, 73, 78, 107 )
AND SUBSTRING( meeting.meetingCode, 5, 2 )
BETWEEN 12
AND 22
AND SUBSTRING( meeting.meetingCode, 7, 2 )
BETWEEN 01
AND 12
AND SUBSTRING( meeting.meetingCode, 9, 2 )
BETWEEN 01
AND 31
GROUP BY user.userID, contactName, email
ORDER BY meetingsCount DESC
You need to put the logic for the meeting code table in your join. Otherwise users matching the records you are filtering out from the meeting table will be filtered out of your results. Making your JOIN essentially an INNER join. I think you also should put single quotes around the values in your BETWEEN clauses.
SELECT user.userID, user.contactName, user.email, COUNT( meeting.userID ) AS meetingsCount
FROM user
LEFT OUTER JOIN meeting ON user.userID = meeting.userID
AND SUBSTRING( meeting.meetingCode, 5, 2 ) BETWEEN '12' AND '22'
AND SUBSTRING( meeting.meetingCode, 7, 2 ) BETWEEN '01' AND '12'
AND SUBSTRING( meeting.meetingCode, 9, 2 ) BETWEEN '01' AND '31'
WHERE user.userID NOT IN ( 1, 2, 3, 4, 5, 59, 62, 63, 64, 66, 69, 71, 72, 73, 78, 107 )
GROUP BY user.userID, contactName, email
ORDER BY meetingsCount DESC

Using MySQL Pivot Table Queries for a Monthly Attendance Report

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