MySQL select percentage - mysql

I have a database table "tblfavs" with five columns: id, userid, logoid, favdate, did.
I want to determine the percentage of a user's (userid) favorites (id) that share the same designer id (did), and where userid <> did, displayed from highest percentage to lowest.
In pseudo-query format:
SELECT [percentage], userid, did
FROM tblfavs
WHERE record has the same userid and did
AND userid <> did
GROUP BY userid
ORDER BY [percentage] DESC
I can't get my head around the query to accomplish this. Help appreciated!
Edit:
Sample data
1, 1, 5, 2017-01-01, 2
2, 7, 3, 2017-01-02, 5
3, 1, 8, 2017-01-02, 2
4, 7, 1, 2017-01-02, 3
In this set user 1 (second column) has two entries and both have "2" as the designer id (final column).
Expected output
100%, userid 1, did 2
50%, userid 7, did 5
50%, userid 7, did 3
etc.

This is easier in other DBMS which feature window functions (e.g. COUNT OVER). However, this is not that difficult in MySQL either. You just need two aggregations: Count per userid and did, count per userid, divide.
select
ud.cnt * 100.0 / u.cnt as percentage,
ud.userid,
ud.did
from
(
select userid, did, count(*) as cnt
from tblfavs
group by userid, did
) ud
join
(
select userid, count(*) as cnt
from tblfavs
group by userid
) u on u.userid = ud.userid
order by percentage desc;

Try this (improved from the previous answer to match your exact input-so you get % for each row not agregated)
select
ud.cnt2 * 100.0 / u.cnt as percentage,
ud.userid,
ud.did
from
(
select
out3.userid,out3.did, cnt2
from
(select userid,did from tblfavs) out3
join
(
select
userid, did, count(*) as cnt2
from tblfavs
group by userid, did
) ud on ud.userid = out3.userid and ud.did = out3.did
) ud
join
(
select userid, count(*) as cnt
from tblfavs
group by userid
) u on u.userid = ud.userid
order by percentage desc;

Related

SQL count without group by

I have a table that has user_id and purchase_id. I want to filter the table so that only users with more than 2 purchases (i.e. there are more than 2 rows for that user in the table). I used count and group by, but it does not work in a way I want.
create view myview as
select user_Id, purchase_id, count(*) as count from mytable group by user_Id;
select user_id, purchase_id from myview where count >2;
But it gives me only users (only one user_id) that has more than 2 purchase and it does not give me all of their purchases. For example if the table looks like this:
user_id purchase_id
1 1212
1 1312
2 1232
1 1321
3 1545
3 4234
My query gives me this :
1 1212
3 1545
But I want this:
1 1212
1 1312
1 1321
3 1545
3 4234
change your last sql like this
select mt.user_id, mt.purchase_id
from myview mv
inner join mytable mt
on mt.user_id=mv.user_id where mv.count >5;
SELECT
*
FROM
mytable mt,
(SELECT user_id, count(*) AS purchase_count FROM mytable GROUP BY user_id) ct
WHERE
mt.user_id = ct.user_id AND ct.purchase_count > 5;
SELECT *
FROM MYTABLE
WHERE USER_ID IN (SELECT USER_ID
FROM MYTABLE
GROUP BY USER_ID
HAVING COUNT(*)>=2)
I tested in my netezza,it works. hopefully, it's also working in mysql
Try GROUP BY with HAVING comment.
SELECT user_Id, purchase_id
FROM mytable
GROUP BY user_Id
HAVING count( * ) >5
As far as I can tell you want to list the user id's and purchase id's of all users that have over 5 purchases.
In order to do this you could do a join on two queries.
For example:
SELECT tblLeft.user_id,
tblLeft.purchase_id
FROM myview tblLeft
JOIN (SELECT user_id,
Count(*) AS purchases
FROM myview
GROUP BY user_id
HAVING purchases > 1) tblRight
ON tblLeft.user_id = tblRight.user_id
The tblRight is essentially a table containing the user_id's of all users with over 5 purchases.
We then do a select (tblLeft) and join it on the tbl right, ensuring only customers with over 5 purchases remain.

Fill in missing rows with zeros

I am trying to get ratings, for a particular item in my database. Not all items have a rating for 1, 2, 3, 4 or 5; when that happens, how can I fill in the row with 0's?
So, for example when I have ratings for 1,2,3 but not for 4 and 5, how could I fill in those two rows with 0's?
select
rating,
count(rating) as rating_count,
(count(rating) / (select count(item_id) from ratings where item_id = 3) * 100) as percent,
avg(rating)
from ratings
where item_id = 3
group by rating desc
with rollup
Here is the above result for the above query, as you can see there is no 1 and 2 rating, how can I get those where rating_count, percent and avg(rating) are zeros?
#Hogan's answer:
This is what I have got working (based on #Hogan's original query):
select
r_list.r as rating,
ifnull(count(rating), 0) as rating_count,
ifnull((count(rating) / (select count(item_id) from ratings where item_id = ?) * 100), 0) as percent,
ifnull(avg(rating), 0)
from (SELECT 1 AS r
UNION ALL
SELECT 2 AS r
UNION ALL
SELECT 3 AS r
UNION ALL
SELECT 4 AS r
UNION ALL
SELECT 5 AS r
) r_list
left join ratings on r_list.r = ratings.rating and item_id = ?
group by r_list.r desc
with rollup
You will use someting along the lines of this to replace the null values as a 0:
SELECT ISNULL(rating, 0 ) FROM myTable

MYSQL - Group By / Order By not working

I have the following data inside a table:
id person_id item_id price
1 1 1 10
2 1 1 20
3 1 3 50
Now what I want to do is group by the item ID, select the id that has the highest value and take the price.
E.g. the sum would be: (20 + 50) and ignore the 10.
I am using the following:
SELECT SUM(`price`)
FROM
(SELECT id, person_id, item_id, price
FROM `table` tbl
INNER JOIN person p USING (person_id)
WHERE p.person_id = 1
ORDER BY id DESC) x
GROUP BY item_id
However, this query is still adding (10 + 20 + 50), which is obviously not what I need to have.
Any ideas to where I am going wrong?
Here is what you are trying to achieve. First you need grouping in a subquery and not in outer query. In outer query you need only sum:
SELECT SUM(`price`)
FROM
(SELECT MAX(price) as price
FROM `table` tbl
INNER JOIN person p USING (person_id)
WHERE p.person_id = 1
GROUP BY item_id) x
http://sqlfiddle.com/#!9/40803/5
SELECT SUM(t1.price)
FROM tbl t1
LEFT JOIN tbl t2
ON t1.person_id= t2.person_id
AND t1.item_id = t2.item_id
AND t1.id<t2.id
WHERE t1.person_id = 1
AND t2.id IS NULL;
I'm not sure if this is the only requirement you have. If so, try this.
SELECT SUM(price)
FROM
(SELECT MAX(price)
FROM table
WHERE person_id = 1
GROUP BY item_id)
First of all - you don't need the person table, because the other table already contains the person_id. So i removed it from the examples.
Your query returns a sum of prices for each item.
If you replace SELECT SUM(price) with SELECT item_id, SUM(price) you wil get
item_id SUM(`price`)
1 30
3 50
But that is not what you want. Neither is it what you wrote in the question " (10 + 20 + 50)".
Now replacing the first line with SELECT id, item_id, SUM(price) you will get one row for each item with the highest id.
id item_id price
2 1 20
3 3 50
This works because of the "undocumented feature" of MySQL, wich allows you to select columns that are not listed in the GROUP BY clause and get the first row from the subselect each group (each item in this case).
Now you only need to sum the price column in an additional outer select
SELECT SUM(price)
FROM (
SELECT id, item_id ,price
FROM (
SELECT id, person_id, item_id, price
FROM `table` tbl
WHERE tbl.person_id = 1
ORDER BY id DESC ) x
GROUP BY item_id
) y
However i do not recomend to use that "feature". While it still works on MySQL 5.6, you never know if that will work with newer versions. It already doesn't work on MariaDB.
Instead you can determite the MAX(id) for each item in an subselect, select only the rows with the determined ids and get the summed price of them.
SELECT SUM(`price`)
FROM `table` tbl
WHERE tbl.id IN (
SELECT MAX(tbl2.id)
FROM `table` tbl2
WHERE tbl2.person_id = 1
GROUP BY tbl2.item_id
)
Another solution (wich internaly does the same) is
SELECT SUM(`price`)
FROM `table` tbl
JOIN (
SELECT MAX(tbl2.id) as id
FROM `table` tbl2
WHERE tbl2.person_id = 1
GROUP BY tbl2.item_id
) x ON x.id = tbl.id
Alex's solution also works fine, if the groups (number of rows per person and item) are rather small.
You have used group by in main query, but it is on subquery like
SELECT id, person_id, item_id, SUM(`price`) FROM ( SELECT MAX(price) FROM `table` tbl WHERE p.person_id = 1 GROUP BY item_id ) AS x

mysql Query , calculate percentage of three grades

I want to calculate percentage of three different grades A,B,C
1 product have A grade
5 products have B grade
3 products gave C grade
SQL - This Query return 3 rows
SELECT count(*) FROM table1 WHERE pid = '480' AND grade IN
( SELECT DISTINCT grade FROM table1 )
GROUP BY grade
------------------------
CCA_ST2 count(*)
------------------------
A 1
B 5
C 3
------------------------
How can i calculate percentages ?
( SELECT count(*) FROM table1 WHERE pid = '480' AND grade IN
( SELECT DISTINCT grade FROM table1 )
GROUP BY grade
)
DIVIDE BY
( SELECT COUNT(*) FROM table1 WHERE pid = '480' ) * 100
I tried with this query
SELECT grade,
( SELECT count(*) FROM table1 WHERE pid = '480' AND grade IN
( SELECT DISTINCT grade FROM table1 )
GROUP BY grade
)
/
( SELECT count(*) FROM table1 WHERE pid = '480' )
* 100
AS score
FROM table1 GROUP BY grade
I got this error . How can i solve this
subquery returns more than one row
i don't really want to create procedure for this , i know this can be done by query . But i'm feeling so lazy today.
You can't take the count from all groups and divide them by the total count, you have to divide each count by the total count:
SELECT
grade,
100 * count(*) / (SELECT count(*) FROM table1 WHERE pid = '480') AS score
FROM table1
WHERE pid = '480'
GROUP BY grade
ORDER BY grade

Query for finding duplicates

I am having a table with following schema:
CUSTOMERS (id INT, name VARCHAR(10), height VARCHAR(10), weight INT)
id is the primary key. I want to find out rows in which people who are having exactly same name, same height and same weight. In other words, I want to find out duplicates with-respect-to name, height and weight.
Example table:
1, sam, 160, 100
2, ron, 167, 88
3, john, 150, 90
4, sam, 160, 100
5, rick, 158, 110
6, john, 150, 90
7, sam, 166, 110
Example Output:
Now since there are people with same name, same height and same weight:
sam (id=1), sam (id=4)
and
john (id=3), john (id=6)
I want to get these ids. It is also okay if I get only one id per match (i.e. id=1 from first match and id=3 from second match).
I am trying this query but not sure if it is correct or not.
SELECT id
FROM customers
GROUP BY name, height, weight
Try this (valid for sql server):
SELECT
t.NAME,
'Ids = '+
(
SELECT cast(Id as varchar)+','
FROM Customers c
WHERE c.NAME = t.NAME AND c.Weight = t.Weight AND c.Height = t.Height
FOR XML PATH('')
)
FROM
(
SELECT Name, height, weight
FROM Customers
GROUP BY Name, height, weight
HAVING COUNT(*) > 1
) t
OR
as you asked - only one Id per match
SELECT
t.NAME,
c.Id
FROM
(
SELECT Name, height, weight
FROM Customers
GROUP BY Name, height, weight
HAVING COUNT(*) > 1
) t
JOIN Customers c ON t.NAME AND c.Weight = t.Weight AND c.Height = t.Height
SELECT *
FROM customers C
INNER JOIN
(
SELECT name, height, weight
FROM customers
GROUP BY name, height, weight
HAVING COUNT(*) > 1
) X ON C.name = X.name AND C.height = X.height AND C.weight = X.weight
SELECT c.*
FROM customers c
JOIN (
SELECT name, height, weight
FROM
GROUP BY name, height, weight
HAVING count(*) > 1
) t ON c.name = t.name and c.height = t.height and c.weight = t.weight
you are on the right way:
SELECT min(id)
FROM customers
GROUP BY name, height, weight
HAVING COUNT(*) > 1
I don't know what you are using since you tagged several databases.
In Sql server you won't be able to select the id without putting it in the SELECT.
so if you want to select other fields besides the ones in the group clasue you can use PARTITION BY. Something like this:
SELECT id,
ROW_NUMBER() OVER(PARTITION BY c.name, c.height, c.weight ORDER BY c.name) AS DuplicateCount
FROM customers c
This will give you the ids of the duplicates that you have with the same name, height and weight.
I'm not sure that this is faster that the other solutions though, but, you can profile it and compare.
If it is okay to get only one id per match as you say, you are close to solution:
SELECT
min( id )
,name, height, weight --<-- oncly if you need/want
FROM customers
GROUP BY name, height, weight
HAVING count(*) > 1