Grouping results by id and create new columns - mysql

I have a table which looks something like the following...
id price condition sell
21039 20.40 new 0
21039 20.41 used 1
12378 10.40 new 1
12378 5 used 0
45898 30.30 new 1
45898 12.20 used 0
(note: there will only ever be 1 new and used value for each id)
What I am trying to do is group all rows with the same id number but in the process creating new columns for each condition, which should look something like...
id new_price new_sell used_price new_sell
21039 20.40 0 20.41 1
12378 10.40 1 5 0
45898 30.30 1 12.20 0
All that I have come up with is the following query, which looks silly
SELECT id, price, condition,
IF(price > 3, 1, 0) AS sell
FROM products
GROUP BY id
How can I get the desired affect of the 2nd table.

This is known as a pivot table. It is done with a series of CASE statements for each column you need to produce, along with an aggregate MAX() or SUM() to eliminate NULLs and collapse it down to a single row.
SELECT
id,
SUM(CASE WHEN `condition` = 'new' THEN price ELSE 0 END) AS new_price,
SUM(CASE WHEN `condition` = 'new' THEN sell ELSE 0 END) AS new_sell,
SUM(CASE WHEN `condition` = 'used' THEN price ELSE 0 END) AS used_price,
SUM(CASE WHEN `condition` = 'used' THEN sell ELSE 0 END) AS used_sell
FROM
products
GROUP BY id
Without the SUM() and GROUP BY, you would still get 2 rows per id, with each having half its columns (not matched by condition in the CASE) as NULL. The SUM() (could also use MAX() in this case) eliminates the NULLs and produces one row since aggregate functions exclude NULL values while the GROUP BY groups the rows by id.
Here is a working sample on SQLFiddle.com
Update after comment:
To calculate sell based on the price, just replace the condition in the sell CASE statements:
SELECT
id,
SUM(CASE WHEN `condition` = 'new' THEN price ELSE 0 END) AS new_price,
SUM(CASE WHEN `condition` = 'new' AND price > 3 THEN 1 ELSE 0 END) AS new_sell,
SUM(CASE WHEN `condition` = 'used' THEN price ELSE 0 END) AS used_price,
SUM(CASE WHEN `condition` = 'used' AND price > 3 THEN 1 ELSE 0 END) AS used_sell
FROM
products
GROUP BY id
(Updated sample...)

Related

SQL count with having clause wrong result

I'm trying to understand how to count mysql row's according to HAVING.
SELECT COUNT(*),
SUM(CASE WHEN sentby='$user_id' AND hiddenbysentby=0 THEN 1 ELSE 0 END) as sentbyuser,
SUM(CASE WHEN sentto='$user_id' AND hiddenbysentto=0 THEN 1 ELSE 0 END) as senttouser
FROM cb_users.user_pm
WHERE title LIKE '%$search%'
HAVING senttouser = 1 OR sentbyuser = 1
I want to count rows that are matching these criteria
SUM(CASE WHEN sentby='$user_id' AND hiddenbysentby=0 THEN 1 ELSE 0 END)
SUM(CASE WHEN sentto='$user_id' AND hiddenbysentto=0 THEN 1 ELSE 0 END)
I have second function that display this data without COUNT(*), and it works fine.
But this query selects all rows no matter if hiddenbysentby = 1 or 0 AND hiddenbysentto = 1 or 0
You should change your HAVING clause to:
HAVING senttouser >= 1 OR sentbyuser >= 1
When you are summing rows, your values are going to equal the total number of rows located for each, therefore, unless you only had exactly one row that meets your criteria, you won't get the result you're looking for.
You should move condition by hiddenbysentby and hiddenbysentto fields in WHERE clause and add a condition by user id
SELECT COUNT(*),
SUM(CASE WHEN sentby='$user_id' THEN 1 ELSE 0 END) as sentbyuser,
SUM(CASE WHEN sentto='$user_id' THEN 1 ELSE 0 END) as senttouser
FROM cb_users.user_pm
WHERE title LIKE '%$search%' AND (hiddenbysentby=0 AND sentby='$user_id') OR (hiddenbysentto=0 AND sentto='$user_id')
HAVING senttouser = 1 OR sentbyuser = 1

SQL using CASE in count and group by

I'm using CASE to categorize data in the table and count them but the results aren't accurate
live demo [here]
select DATE(date) as day, count(*),
count(distinct case when name = 'fruit' then 1 else 0 end) as fruits,
count(distinct case when name = 'vege' then 1 else 0 end) as vege,
count(distinct case when name = 'sweets' then 1 else 0 end) as sweets
from food
group by day
with rollup
I'm not sure if the issue is with CASE or in the string matching = because there's no 'sweets' still it counts 1?
any pointers I'd be grateful
Your problem is that COUNT counts every result that is not NULL. In your case you are using:
COUNT(distinct case when name = 'sweets' then 1 else 0 end)
So, when the name is not sweets, it counts the 0. Furthermore, since you are using DISTINCT, it counts just one or two values. You should either use SUM or remove the DISTINCT and the ELSE 0:
SELECT DATE(date) as day,
COUNT(*),
SUM(CASE WHEN name = 'fruit' THEN 1 ELSE 0 END) as fruits,
SUM(CASE WHEN name = 'vege' THEN 1 ELSE 0 END) as vege,
SUM(CASE WHEN name = 'sweets' THEN 1 ELSE 0 END) as sweets
FROM food
GROUP BY DAY
WITH ROLLUP
Or:
SELECT DATE(date) as day,
COUNT(*),
COUNT(CASE WHEN name = 'fruit' THEN 1 ELSE NULL END) as fruits,
COUNT(CASE WHEN name = 'vege' THEN 1 ELSE NULL END) as vege,
COUNT(CASE WHEN name = 'sweets' THEN 1 ELSE NULL END) as sweets
FROM food
GROUP BY DAY
WITH ROLLUP
Here is a modified sqlfiddle.
You can't group by an alias. You have to group by the expression.
group by date(date)
You can group on an Alias:
SELECT
FROM_UNIXTIME(UnixTimeField, '%Y') AS 'Year'
,FROM_UNIXTIME(UnixTimeField, '%m') AS 'Month'
FROM table p
GROUP BY Year, Month

Pivoting Data Using MySql

Objective 1:
Your sales data is stored in the Purchases table.
Your sales staff wants to see the sales data in a pivoted form, broken down by quarter.
If your Purchases table doesn't have sales data, create some. Be sure the data spans four quarters.
Next, write a query to pivot the data as follows:
Album Q1 Q2 Q3 Q4
OK Computer 2 5 3 7
Sea Change 8 6 2 1
Do not create a separate table or a view. Do not alter any tables.
Save your query as dba1lesson10project1.sql and hand in the project.
This is What I need to do. But, the table it wants me to work with looks like this. And it states in the assignment I cannot alter it at all.
CustomerID DateOfPurchase SongID
1 2007-03-31 3
3 2007-06-30 4
4 2007-09-30 4
5 2007-12-31 5
I have tried
SELECT SongID,
SUM(CASE WHEN DateOfPurchase = '2007-03-31' THEN DateOfPurchase ElSE 0 END) AS 'Q1',
SUM(CASE WHEN DateOfPurchase = '2007-06-30' THEN DateOfPurchase ELSE 0 END) AS 'Q2',
SUM(CASE WHEN DateOfPurchase = '2007-09-30' THEN DateOfPurchase ELSE 0 END) AS 'Q3',
SUM(CASE WHEN DateOfPurchase = '2007-12-31' THEN DateOfPurchase ELSE 0 END) AS 'Q4'
FROM Purchases
GROUP BY SongID;
Along with other variants:
SELECT SongID,
SUM(CASE WHEN DateOfPurchase = '2007-03-31' THEN CustomerID ElSE 0 END) AS 'Q1',
SUM(CASE WHEN DateOfPurchase = '2007-06-30' THEN CustomerID ELSE 0 END) AS 'Q2',
SUM(CASE WHEN DateOfPurchase = '2007-09-30' THEN CustomerID ELSE 0 END) AS 'Q3',
SUM(CASE WHEN DateOfPurchase = '2007-12-31' THEN CustomerID ELSE 0 END) AS 'Q4'
FROM Purchases
GROUP BY SongID;
And:
SELECT SongID,
SUM(CASE WHEN DateOfPurchase = '2007-03-31' THEN SongID ElSE 0 END) AS 'Q1',
SUM(CASE WHEN DateOfPurchase = '2007-06-30' THEN SongID ELSE 0 END) AS 'Q2',
SUM(CASE WHEN DateOfPurchase = '2007-09-30' THEN SongID ELSE 0 END) AS 'Q3',
SUM(CASE WHEN DateOfPurchase = '2007-12-31' THEN SongID ELSE 0 END) AS 'Q4'
FROM Purchases
GROUP BY SongID;
Which, the last 2 almost get me what I need. But, there is only one purchase per SongID and Quarter. They show me either the CustomerID or SongID instead. I have a basic understand of what I need to do. But without being able to alter the table to show how many purchases there have actually been I'm not sure what to do. Any suggestions?
Your current query is very close, I would suggest a few minor changes. You are hard-coding the date but what happens if you have a date in quarter one that is not equal to 2007-03-31 it won't show up.
I would use the QUARTER() function in MySQL, this will return the quarter 1-4 based on the date value.
Second, I don't think you want to sum the SongID, in your CASE expression I would replace the SongID with 1 similar to the following:
SELECT SongID,
SUM(CASE WHEN Quarter(DateOfPurchase) = 1 THEN 1 ElSE 0 END) AS Q1,
SUM(CASE WHEN Quarter(DateOfPurchase) = 2 THEN 1 ELSE 0 END) AS Q2,
SUM(CASE WHEN Quarter(DateOfPurchase) = 3 THEN 1 ELSE 0 END) AS Q3,
SUM(CASE WHEN Quarter(DateOfPurchase) = 4 THEN 1 ELSE 0 END) AS Q4
FROM Purchases
GROUP BY SongID;
See SQL Fiddle with Demo
You can do this quarterly report easily using MySQL Pivot table generator.
While creating your report, you will be asked to add the "Column settings "
Please choose the "Purchasing table" as the "Table", the column that contains the date values as the "Field" . Once selecting a date value, a function menu should appear, please select "Quarter" to get your pivot table divided in quarters as you requested. please note that you have the option to create a report for one specific year if you like and in this case you should check the "Exact Year" Box and add a specific year otherwise leave the "Exact year" box unchecked .
The final report will be able to see something like this report :
http://mysqlpivottable.net/MySQL-Pivot-Table-Demo/tables/Quarterly_Sales_Report/

Grouping similar values into new select statement

I have a table which looks something like this...
supplier_name status count
supplier_a Unavailable 1
supplier_a Refunded 5
supplier_a Shipped 10
supplier_z Refunded 2
supplier_z Shipped 4
I know exactly what columns I want and I would like to use the values from the first table to return values structured like the following...
supplier_name unavailable refunded shipped
supplier_a 1 5 10
supplier_z 0 2 4
(note: in cases where there is no value for a specific column it should be set to 0)
Would this be possible to achieve in a query?
I think something like this should work:
SELECT Supplier_Name,
MAX(CASE WHEN STATUS = 'Unavailable' THEN Count ELSE 0 END) as Unavailable,
MAX(CASE WHEN STATUS = 'Refunded' THEN Count ELSE 0 END) as refunded,
MAX(CASE WHEN STATUS = 'Shipped' THEN Count ELSE 0 END) as shipped
FROM YourTable
GROUP BY Supplier_Name
And here is the SQL Fiddle.
Good luck.
Try this:
SELECT
supplier_name,
SUM(COALESCE(CASE WHEN status = 'unavailable' THEN `count` END, 0)) unavailable,
SUM(COALESCE(CASE WHEN status = 'refunded' THEN `count` END, 0)) refunded,
SUM(COALESCE(CASE WHEN status = 'shipped' THEN `count` END, 0)) shipped
FROM tbl
GROUP BY supplier_name
SQL FIDDLE DEMO

MySQL multiple count in single query

I have a table named 'sales' with following fields
salesid (type: int)
stime (type: datetime)
status (type: enum, values: remaining OR cancelled OR done)
... and other fields
I need a query that can output as
SalesDate TotalSales TotalDone TotalRemaining TotalCancelled
2010-11-06 10 5 3 2
2010-11-06 15 14 1 0
Anyone know how to achieve this?
Help appreciated, thanks.
You can use conditional summation to get the results you want.
select stime as sales_date
,sum(1) as total_sales
,sum(case when status = 'remaining' then 1 else 0 end) as total_done
,sum(case when status = 'cancelled' then 1 else 0 end) as total_remaining
,sum(case when status = 'done' then 1 else 0 end) as total_cancelled
from sales
group by stime;
You can also use COUNT(expr) and exploit the fact it doesn't include NULLS, but personally I find that the above best conveys the intent of the developer.
Use a CASE statement in your SELECT to create pseudo columns using the status values of remaining, cancelled and done
SELECT
date(stime),
COUNT(salesid),
SUM(CASE status WHEN 'done' THEN 1 ELSE 0 END) as TotalDone,
SUM(CASE status WHEN 'remaining' THEN 1 ELSE 0 END) as TotalRemaining,
SUM(CASE status WHEN 'cancelled' THEN 1 ELSE 0 END) as TotalCancelled
FROM sales
GROUP BY date(stime)
Note: This is what i was trying to refer to. I think this should work but i have no access to MySQL to try out the syntax and verify it. Sorry about that. But this should get you going.