Msql Crosstab month rows as columns? - mysql

Good morning, I am unsuccessfully trying to create a report using mysql/php, I would like to have rows as columns similar to Access Crosstab or Excel Pivot. I have sales staff & monthly sales which I would like to display across the page with month as column headers.
My query below outputs the data but every salesperson has a row for every month which doesnt read very well,
Jan | Feb | Mar | April
Sales1
Sales2
Sales3
Sales4
Select
tblcontacts.ContactFullName,
Count(tblcases.CaseID) As cases,
MonthName(tblcases.CaseDate) As Monthly
From
tblcases Inner Join
tblcontacts On tblcases.ContactAssignedTo =
tblcontacts.ContactID
Group By
tblcontacts.ContactFullName,
MonthName(tblcases.CaseDate)
with rollup
Ant advice or pointers appreciated, I have researched but most of it went right over my head!
Kind regards

What you can do is simply group by each contact and use conditional aggregation to count rows based on each month:
SELECT
a.ContactFullName,
SUM(MONTH(b.CaseDate) = 1) AS Jan,
SUM(MONTH(b.CaseDate) = 2) AS Feb,
SUM(MONTH(b.CaseDate) = 3) AS Mar,
SUM(MONTH(b.CaseDate) = 4) AS Apr,
SUM(MONTH(b.CaseDate) = 5) AS May,
SUM(MONTH(b.CaseDate) = 6) AS Jun,
SUM(MONTH(b.CaseDate) = 7) AS Jul,
SUM(MONTH(b.CaseDate) = 8) AS Aug,
SUM(MONTH(b.CaseDate) = 9) AS Sep,
SUM(MONTH(b.CaseDate) = 10) AS Oct,
SUM(MONTH(b.CaseDate) = 11) AS Nov,
SUM(MONTH(b.CaseDate) = 12) AS Dec
FROM
tblcontacts a
INNER JOIN
tblcases b ON a.ContactID = b.ContactAssignedTo
GROUP BY
a.ContactFullName
Edit: As per your comments to this answer: to get a price sum of each month, you can do something like:
SELECT
a.ContactFullName,
SUM(IF(MONTH(b.CaseDate) = 1, b.price, 0)) AS Jan,
SUM(IF(MONTH(b.CaseDate) = 2, b.price, 0)) AS Feb,
SUM(IF(MONTH(b.CaseDate) = 3, b.price, 0)) AS Mar,
SUM(IF(MONTH(b.CaseDate) = 4, b.price, 0)) AS Apr,
SUM(IF(MONTH(b.CaseDate) = 5, b.price, 0)) AS May,
SUM(IF(MONTH(b.CaseDate) = 6, b.price, 0)) AS Jun,
SUM(IF(MONTH(b.CaseDate) = 7, b.price, 0)) AS Jul,
SUM(IF(MONTH(b.CaseDate) = 8, b.price, 0)) AS Aug,
SUM(IF(MONTH(b.CaseDate) = 9, b.price, 0)) AS Sep,
SUM(IF(MONTH(b.CaseDate) = 10, b.price, 0)) AS Oct,
SUM(IF(MONTH(b.CaseDate) = 11, b.price, 0)) AS Nov,
SUM(IF(MONTH(b.CaseDate) = 12, b.price, 0)) AS Dec
FROM
tblcontacts a
INNER JOIN
tblcases b ON a.ContactID = b.ContactAssignedTo
GROUP BY
a.ContactFullName
Basically, for each row, if the casedate is in a particular month, pass the value of the price column to the SUM aggregation, otherwise, just pass it 0.

Select
tblcontacts.ContactFullName,
sum(case when MonthName(tblcases.CaseDate)='January' then 1 else 0 end) as January,
sum(case when MonthName(tblcases.CaseDate)='February' then 1 else 0 end) as February,
.
.
sum(case when MonthName(tblcases.CaseDate)='December' then 1 else 0 end) as December,
From
tblcases Inner Join
tblcontacts On tblcases.ContactAssignedTo =
tblcontacts.ContactID
Group By
tblcontacts.ContactFullName

Related

Join another table and same table

need your help here
I've been working on this query for a day lol
I have 2 tables
table Users
and table payroll_variable
My goals here to Join table users with 2 or more table payroll_variable like this
This is what I do so far :
SELECT DISTINCT
u.id_user,
u.first_name,
u.last_name,
pv.year,
pv.base_salary,
CASE
WHEN pv.month = 'January' THEN pv.total_salary
ELSE NULL
END as 'January',
CASE
WHEN pv1.month = 'February' THEN pv1.total_salary
ELSE NULL
END as 'February'
FROM payroll_variable pv
INNER JOIN users u ON u.id_user = pv.id_user
INNER JOIN payroll_variable pv1 ON pv1.id_user = pv.id_user
WHERE pv.status != 'submit'
And this the wrong result from my query
The problem is that , i just want to show every user with only one loop like my goals, and don't mind my WHERE clause , it's just for flag-ing
First of all I'd changed name of two fields in my query i.e. month and year to payroll_month and payroll_year.
Here is the query that give you desired output
Select u.id_user, u.first_name, u.last_name, pv.payroll_year, pv.base_salary,
sum(IF(pv.payroll_month = 'January', pv.total_salary, 0)) as 'January',
sum(IF(pv.payroll_month = 'February', pv.total_salary, 0)) as 'February',
sum(IF(pv.payroll_month = 'March', pv.total_salary, 0)) as 'March',
sum(IF(pv.payroll_month = 'April', pv.total_salary, 0)) as 'April',
sum(IF(pv.payroll_month = 'May', pv.total_salary, 0)) as 'May',
sum(IF(pv.payroll_month = 'June', pv.total_salary, 0)) as 'June',
sum(IF(pv.payroll_month = 'July', pv.total_salary, 0)) as 'July',
sum(IF(pv.payroll_month = 'August', pv.total_salary, 0)) as 'August',
sum(IF(pv.payroll_month = 'September', pv.total_salary, 0)) as 'September',
sum(IF(pv.payroll_month = 'October', pv.total_salary, 0)) as 'October',
sum(IF(pv.payroll_month = 'November', pv.total_salary, 0)) as 'November',
sum(IF(pv.payroll_month = 'December', pv.total_salary, 0)) as 'December'
from users u join payroll_variable pv using(id_user)
group by u.id_user
And sample output as in image
The problem is that you do not ensure that you have a record when there was no payment in either january or february and also, you do not ensure that you have a single record for each logical pair of january and february for the same user. You will need to use LEFT JOIN to ensure you have a row for each user and you will need to ensure that you have a single row for each logical tuples. I do not understand the logic behind your where condition, so I might be wrong there. I assume that a single record is stored for january or february if there was any payment:
SELECT DISTINCT
u.id_user,
u.first_name,
u.last_name,
pv.year as year,
pv.base_salary,
CASE
WHEN pv.month = 'January' THEN pv.total_salary
ELSE 0
END as January,
CASE
WHEN pv.month = 'February' THEN pv.total_salary
ELSE 0
END as February,
FROM users u
LEFT JOIN payroll_variable jan ON u.id_user = jan.id_user
LEFT JOIN payroll_variable feb ON feb.id_user = u.id_user
WHERE jan.status <> 'submit' and feb.status <> 'submit' and (not (jan.id_user is null) or not (feb.id_user is null))
This code is not tested and it is possible that I misunderstood your intention.
EDIT
In order to group your results by users, you will need to use group by:
select id_user, first_name, last_name, max(t.year), max(base_salary), sum(January) as January, sum(February) as February
from (
SELECT
u.id_user,
u.first_name,
u.last_name,
min(pv.year) as year,
min(pv.base_salary) as base_salary,
sum(case when pv.payroll_month = 'January' then pv.total_salary else 0 end) as January,
sum(case when pv.payroll_month = 'February' then pv.total_salary else 0 end) as February
FROM users u
LEFT JOIN payroll_variable pv ON u.id_user = pv.id_user) t
group by t.id_user, t.first_name, t.last_name

get Month and Year in Colums // Mysql

I have the following Select in Mysql, which enables me to list an amount for each customer by month. It works fine as it is.
I would like to have in each column not only by month but also adding the corresponding year (instead of 'Jan' in Col 1, 'Jan 2015', 'Feb 2015' in Col 2, etc...)
Also, As a first line, how could I group the sum of the corresponding column to have this
Jan-2015 Feb-2015 Mar-2015
Total 1000 1600 ...
Customer1 100 800
Customer2 900 600
Customer3 0 200
How could I achieve this? I can have a Total line with PHP, but getting it in a Select would save me a lot of time...
Here is my initial select
SELECT
Customers,
sum(if(month(Date) = 1, Total_Deposits, 0)) AS Jan,
sum(if(month(Date) = 2, Total_Deposits, 0)) AS Feb,
sum(if(month(Date) = 3, Total_Deposits, 0)) AS Mar,
sum(if(month(Date) = 4, Total_Deposits, 0)) AS Apr,
sum(if(month(Date) = 5, Total_Deposits, 0)) AS May,
sum(if(month(Date) = 6, Total_Deposits, 0)) AS Jun,
sum(if(month(Date) = 7, Total_Deposits, 0)) AS Jul,
sum(if(month(Date) = 8, Total_Deposits, 0)) AS Aug,
sum(if(month(Date) = 9, Total_Deposits, 0)) AS Sep,
sum(if(month(Date) = 10, Total_Deposits, 0)) AS Oct,
sum(if(month(Date) = 11, Total_Deposits, 0)) AS Nov,
sum(if(month(Date) = 12, Total_Deposits, 0)) AS `Dec`
FROM Online_customer_activity_v2
where Date BETWEEN '2015-01-01' AND '2015-12-31'
Group by Customers
ORDER by sum(`Online_customer_activity_v2`.`Total_Deposits`) DESC
LIMIT 20
Thanks to everyone in advance for your tips!

mysql where clause spanning multiple years?

I have mysql query that outputs years leads v sales and calculates the conversions rates which worked great in 2012 as it only held a years data. Now its 2013 I need to change how it works but have become stuck. If a lead came in December 2012, then became a sale in 2013 its is ommitted from my query output as it only asks for CaseDate of 2013.
Select
q.*,
ROUND(100 * q.Comms / q.Total, 2) As Conversion,
If(q.Adviser Is Null, 1, 0) As remove
From
(Select
a.ContactFullName As Adviser,
Sum(Year(b.CaseDate) = 2013 And Month(b.CaseDate) = 1) As Jan,
Sum(Year(b.CaseDate) = 2013 And Month(b.CaseDate) = 2) As Feb,
Sum(Year(b.CaseDate) = 2013 And Month(b.CaseDate) = 3) As Mar,
..... Dec
Sum(Case
When Year(b.CaseDate) = 2013 And Month(b.CaseDate) Between '1'
And '12' Then 1 Else 0 End) As Total,
Sum(Case When (Year(b.CaseDate) = 2012 Or Year(b.CaseDate) = 2013) And Year(b.StatusSubmittedDate) = ".(int)$_POST['year']." And Month(b.StatusSubmittedDate) Between ".(int)$_POST['start']." And ".(int)$_POST['end']." Then 1 Else 0 End) As Comms
From
tblcontacts a Inner Join
tblcases b On a.ContactID = b.ContactAssignedTo
Group By
a.ContactFullName With Rollup) q
This works;
Sum(Case When (Year(b.CaseDate) = 2012 Or Year(b.CaseDate) = 2013) And Year(b.StatusSubmittedDate) = 2013 And Month(b.CaseDate) Between '1' And '12' Then 1 Else 0 End) As Comms
This donly shows leads entered & submitted in 2013;
Sum(Case When (Year(b.CaseDate) = 2012 Or Year(b.CaseDate) = 2013) And Year(b.StatusSubmittedDate) = ".(int)$_POST['year']." And Month(b.CaseDate) Between ".(int)$_POST['start']." And ".(int)$_POST['end']." Then 1 Else 0 End) As Comms
Many Thanks
It depends on your business logic. If the logic says you only show leads that came in the same year, then your code is perfectly correct as-is (as-was?). If you want to include 2012 or 2013 (which it looks like you do), you need to spell it out completely, the server doesn't understand "2012 or 2013"
Sum(Case When (Year(b.CaseDate) = 2012 Or Year(b.CaseDate) = 2013) And Year(....
Notice the parenthesis around the Or statement, this is to make sure that CaseYear=2012 or CaseYear=2013 and SubmitYear=2013 is treated as (CaseYear=2012 or CaseYear=2013) and SubmitYear=2013 instead of CaseYear=2012 or (CaseYear=2013 and SubmitYear=2013)
Of course if the business logic does not care about the CaseYear, then take that out of the Sum.

Mysql where clause breaks calculated query

wonder if someone can point out the error in my code below. I have mysql query that displays sales leads by month, then calculates % of those converted to sales. If I add where clause to this it breaks the total column as it outputs the same as the Comms column?
Select
q.*,
ROUND(100 * Comms / Total, 2) As Conversion,
If(q.Adviser Is Null, 1, 0) As remove
From
(Select
a.ContactFullName As Adviser,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 1) AS Jan,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 2) As Feb,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 3) As Mar,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 4) As Apr,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 5) As May,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 6) As Jun,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 7) As Jul,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 8) As Aug,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 9) As Sep,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 10) As Oct,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 11) As Nov,
SUM(YEAR(b.CaseDate) = 2013 AND Month(b.CaseDate) = 12) As Dece,
Count(b.CaseID) As Total,
Sum(Case When Year(b.StatusSubmittedDate) = 2013 Then 1 Else 0
End) As Comms
From
tblcontacts a Inner Join
Without WHERE clause it outputs;
Total - Comms - Conversion
479 - 148 - 30.9%
With WHERE clause it outputs;
Total - Comms - Conversion
148 - 148 - 100%
I cant work out why this has happened?
Kind Regards
UPDATE:
I rewrote your whole query:
Select
q.*,
totals.total,
ROUND(100 * Comms / totals.total, 2) As Conversion
If(q.Adviser Is Null, 1, 0) As remove
From
(Select
a.ContactFullName As Adviser,
Sum(Month(b.CaseDate) = 1) As Jan,
Sum(Month(b.CaseDate) = 2) As Feb,
Sum(Month(b.CaseDate) = 3) As Mar,
Sum(Month(b.CaseDate) = 4) As Apr,
Sum(Month(b.CaseDate) = 5) As May,
Sum(Month(b.CaseDate) = 6) As Jun,
Sum(Month(b.CaseDate) = 7) As Jul,
Sum(Month(b.CaseDate) = 8) As Aug,
Sum(Month(b.CaseDate) = 9) As Sep,
Sum(Month(b.CaseDate) = 10) As Oct,
Sum(Month(b.CaseDate) = 11) As Nov,
Sum(Month(b.CaseDate) = 12) As Dece,
Count(b.StatusSubmittedDate) As Comms
From
tblcontacts a Inner Join
tblcases b On a.ContactID = b.ContactAssignedTo
WHERE Year(b.StatusSubmittedDate) = 2013
Group By
a.ContactFullName With Rollup) q
inner join (select a.ContactFullName As Adviser, count(*) as total from
tblcontacts a Inner Join
tblcases b On a.ContactID = b.ContactAssignedTo
WHERE Year(b.StatusSubmittedDate) = 2013
group by Adviser
)totals on q.Adviser = totals.Adviser
Original answer:
To have multiple COUNTS you can workaround like this:
Count(b.CaseID) As Total,
SUM(CASE WHEN YEAR(b.StatusSubmittedDate) = 2013 THEN 1 ELSE 0 END) As Comms,
but this:
ROUND(100 * Count(b.StatusSubmittedDate) / Count(b.CaseID),
won't work, since this calculation has to be done later.
Select
q.*,
ROUND(100 * Comms / Total, 2) As Conversion,
If(q.Adviser Is Null, 1, 0) As remove
From
(Select
a.ContactFullName As Adviser,
Sum(Month(b.CaseDate) = 1 and year(b.CaseDate) = 2012) As Jan,
... Dec,
Count(b.CaseID) As Total,
SUM(CASE WHEN YEAR(b.StatusSubmittedDate) = 2013 THEN 1 ELSE 0 END) As Comms
From
tblcontacts a Inner Join
tblcases b On a.ContactID = b.ContactAssignedTo
Group By
a.ContactFullName With Rollup) q

Mysql filter query from drop down box

Good morning, I have the mysql query below to display the years sales which worked fine until the year changed as it now shows Jan 2012 & Jan 2013 combined.
To try to resolve I placed a drop down form box (id=YearSelect) with value of 2012 & 2013, is it possible I can control this query with the value selected? Should the end user select 2012 then that value would be sent to column Year as a type of where clause?
<form id="yearselect">
<select name="year">
<option value="">Select year </option>
<option value="2012">2012</option>
<option value="2013">2013</option>
</select>
</form>
Select
*,
If(q.Adviser Is Null, 1, 0) As remove
From
(Select
a.ContactFullName As Adviser,
YEAR(b.CaseDate) As Year,
Sum(If(Month(b.StatusSubmittedDate) = 1, b.CaseCommission, 0)) As Jan,
Sum(If(Month(b.StatusSubmittedDate) = 2, b.CaseCommission, 0)) As Feb,
Sum(If(Month(b.StatusSubmittedDate) = 3, b.CaseCommission, 0)) As Mar,
Sum(If(Month(b.StatusSubmittedDate) = 4, b.CaseCommission, 0)) As Apr,
Sum(If(Month(b.StatusSubmittedDate) = 5, b.CaseCommission, 0)) As May,
Sum(If(Month(b.StatusSubmittedDate) = 6, b.CaseCommission, 0)) As Jun,
Sum(If(Month(b.StatusSubmittedDate) = 7, b.CaseCommission, 0)) As Jul,
Sum(If(Month(b.StatusSubmittedDate) = 8, b.CaseCommission, 0)) As Aug,
Sum(If(Month(b.StatusSubmittedDate) = 9, b.CaseCommission, 0)) As Sep,
Sum(If(Month(b.StatusSubmittedDate) = 10, b.CaseCommission, 0)) As Oct,
Sum(If(Month(b.StatusSubmittedDate) = 11, b.CaseCommission, 0)) As Nov,
Sum(If(Month(b.StatusSubmittedDate) = 12, b.CaseCommission, 0)) As Decb,
Sum(b.CaseCommission) As Total
From
tblcontacts a Inner Join
tblcases b On a.ContactID = b.ContactAssignedTo
Where
WHERE Year(b.StatusSubmittedDate) = ".(int)$_POST['year']."
Group By
a.ContactFullName WITH ROLLUP
Having
Sum(b.CaseCommission) > 0.01) q
Order By
If(q.Adviser Is Null, 1, 0), q.Total Desc
Any advice appreciated.
What about changing this WHERE:
Where
b.StatusSubmittedDate > '2012 - 01 - 01'
to:
Where
YEAR(b.StatusSubmittedDate) = ".(int)$YEAR_VALUE_FROM_DROPDOWN."
That should be enough...
Considering the PHP tag I assume you are showing the results on a PHP bases page. You can use the following code to do what you want.
<form id="yearselect" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" >
<select name='selyear' onchange='document.getElementById("yearselect").submit()'>
<option value=''> - Select year </option>
<option value='2012'>2012</option>
<option value='2013'>2013</option>
</select>
</form>
<?
$y = date("Y");
if (isset($_POST['selyear']))
{
if (checkdate(1, 1, $_POST['selyear']) && $_POST['selyear']>=2012)
$y = $_POST['selyear'];
}
$query = 'Select
*,
If(q.Adviser Is Null, 1, 0) As remove
From
(Select
a.ContactFullName As Adviser,
YEAR(b.CaseDate) As Year,
Sum(If(Month(b.StatusSubmittedDate) = 1, b.CaseCommission, 0)) As Jan,
Sum(If(Month(b.StatusSubmittedDate) = 2, b.CaseCommission, 0)) As Feb,
Sum(If(Month(b.StatusSubmittedDate) = 3, b.CaseCommission, 0)) As Mar,
Sum(If(Month(b.StatusSubmittedDate) = 4, b.CaseCommission, 0)) As Apr,
Sum(If(Month(b.StatusSubmittedDate) = 5, b.CaseCommission, 0)) As May,
Sum(If(Month(b.StatusSubmittedDate) = 6, b.CaseCommission, 0)) As Jun,
Sum(If(Month(b.StatusSubmittedDate) = 7, b.CaseCommission, 0)) As Jul,
Sum(If(Month(b.StatusSubmittedDate) = 8, b.CaseCommission, 0)) As Aug,
Sum(If(Month(b.StatusSubmittedDate) = 9, b.CaseCommission, 0)) As Sep,
Sum(If(Month(b.StatusSubmittedDate) = 10, b.CaseCommission, 0)) As Oct,
Sum(If(Month(b.StatusSubmittedDate) = 11, b.CaseCommission, 0)) As Nov,
Sum(If(Month(b.StatusSubmittedDate) = 12, b.CaseCommission, 0)) As Decb,
Sum(b.CaseCommission) As Total
From
tblcontacts a Inner Join
tblcases b On a.ContactID = b.ContactAssignedTo
Where
b.StatusSubmittedDate > \''.$y.' - 01 - 01\'
AND b.StatusSubmittedDate < \''.$y.' - 12 - 31\'
Group By
a.ContactFullName WITH ROLLUP
Having
Sum(b.CaseCommission) > 0.01) q
Order By
If(q.Adviser Is Null, 1, 0), q.Total Desc'
//output results
?>
Now I didnt check your query as you mentioned it works. Also instead of posting the form on changing the select list, you could also alter it to use AJAX or whatever to make it better. But I guess you get the idea.
Also I now check for a valid year, not just 2012 or 2013. That depends on what you want.