I have a table containing list of actions performed by persons.
After
SELECT COUNT(NICK) AS TOTAL, NICK, ACTION FROM ACTIONS2 GROUP BY ACTION,NICK ORDER BY NICK ASC, TOTAL DESC
it looks like this:
total nick action
13 0xlne destroyed
5 0xlne captured
5 0xlne deployed
1 13key destroyed
2 74pawel deployed
1 74pawel destroyed
1 74pawel captured
Now, I would like to multiply action destroy x75,capture x500, deploy x125 and so on, so first row should look: (13*75+5*500+5*125=4100)
total nick
4100 0xlne
75 13key
and so on. Is there a way to correlate this few types of actions with values without creating new table, and joining them before multiply and sum?
It's a matter of using CASE/WHEN
SELECT
SUM(CASE WHEN action='destroyed' THEN total*75 WHEN action='captured' THEN total*500 ELSE total*125 END), nick
FROM (SELECT COUNT(NICK) AS TOTAL, NICK, ACTION FROM ACTIONS2 GROUP BY ACTION,NICK ORDER BY NICK ASC, TOTAL DESC) AS nicks
GROUP by nick;
As a footnote: generally upper case is used for SQL reserved words and lower case for column/table names.
Use conditional aggregation and sum the weighted values:
SELECT NICK, COUNT(*) AS totalAction
COALESCE(SUM(ACTION='destroyed'),0)*75 +
COALESCE(SUM(ACTION='capture'),0)*500 +
COALESCE(SUM(ACTION='deploy'),0)*125
as totalPoints
FROM ACTIONS2
GROUP BY NICK
ORDER BY NICK ASC, totalAction DESC
The expression
COALESCE(SUM(ACTION='destroyed'),0)*75
can aslo be written as
COUNT(CASE WHEN ACTION='destroyed' THEN 1 END)*75
Or
COUNT(ACTION='destroyed' OR NULL)*75
Or many other ways.
A little bit different approach:
SELECT NICK, COUNT(*) AS totalAction,
SUM(
CASE ACTION
WHEN 'destroyed' THEN 75
WHEN 'capture' THEN 500
WHEN 'deploy' THEN 125
ELSE 0
END
) as totalPoints
FROM ACTIONS2
GROUP BY NICK
ORDER BY NICK ASC, totalAction DESC
Related
I'm looking into creating a column Status based on a football result where if you have the maximum points, then you're the champion. However, if there are two teams with the maximum points, then another condition where goal differences need to be the highest as well to be the champion. The status will display as '1' if the team is a champion and '0' otherwise.
An example of my original table is as follow:
Team
Total_points
Goal_difference
A
15
3
B
20
2
C
20
5
D
10
9
Afterwards, I'd insert another column called status, and update it based on the total_points only:
alter table_name
add column status int(1) after goal_difference;
update table_name
set status = case
when total_points = (select * from (select max(total_points) from table_name) as X) then '1'
else '0'
end;
How can I modify my command so that if the first condition is satisfied and only when duplicate values of '1' is detected, it will then proceed to check the conditions of the goal difference? The priority is still the total points not the goal difference.
Any feedback is appreciated, thanks!
If you are running MySQL 8+, then ROW_NUMBER would work well here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY Total_points DESC, Goal_difference DESC) rn
FROM yourTable
)
SELECT Team, Total_points, Goal_difference,
CASE WHEN rn = 1 THEN 1 ELSE 0 END AS champion
FROM cte
ORDER BY Team, rn;
Not sure if this is possible but I have the following
SELECT SUM(rating) as rating FROM details WHERE client_id = '$id'
The rating column contains individual ratings of users (1,2,3,4,5) stars. I need to sum them all to be able to calculate the average, but I would also like to get how many stars of each delimiter that user got as well.
For instance
The user has 3 records and the ratings are (3,4,3). With a SUM of 10 out of 3 records, I get 3.3 average. But I would like as well
3 stars = 2
4 stars = 1
Is it possible to do this with one query?
If I got your question right, you can use AVG() function instead of SUM().
In the following query, it calculates the average rating of this client_id, also counts number of each star as required in question.
SELECT
AVG(rating) as rating,
COUNT(CASE WHEN rating=1 THEN 1 END) as star_1,
COUNT(CASE WHEN rating=2 THEN 1 END) as star_2,
COUNT(CASE WHEN rating=3 THEN 1 END) as star_3,
COUNT(CASE WHEN rating=4 THEN 1 END) as star_4,
COUNT(CASE WHEN rating=5 THEN 1 END) as star_5
FROM
details
WHERE
client_id = ID_HERE
GROUP BY
client_id
SELECT client_id ,sum(if(rating='1',1,0)) onestar,sum(if(rating='2',1,0)) twostar,sum(if(rating='3',1,0)) threestar,sum(if(rating='4',1,0)) fourstar,sum(if(rating='5',1,0)) fivestar, SUM(rating) sumrate,AVG(rating) as avgrate FROM details WHERE client_id = '$id'
or for all clients
SELECT client_id ,sum(if(rating='1',1,0)) onestar,sum(if(rating='2',1,0)) twostar,sum(if(rating='3',1,0)) threestar,sum(if(rating='4',1,0)) fourstar,sum(if(rating='5',1,0)) fivestar, SUM(rating) sumrate,AVG(rating) as avgrate FROM details WHERE group by client_id
It is a bit unclear what exactly you are after, but this might work:
SELECT rating, count(rating) FROM details WHERE client_id = '$id' GROUP BY rating;
Also, not sure how you are getting $id, but if you are getting it from the user, you should be using prepared statements rather than inserting it directly in your SQL code.
You don't need to sum your rating. I believe what you want is to key off the rating. I'd suggest this.
This will group every rating then tell you how many times this client is rated something that many times. This would also help you account for any instances where they made a decimal vote (i.e. 4.5 stars)
SELECT
concat(rating, ' Stars') as rating,
count(*) as count
FROM details
WHERE client_id = '$id'
Group by concat(rating, ' Stars');
I think this should do it. Also, you said you're summing them only to take the average. Why not use AVG() instead.
SELECT COUNT(rating), AVG(rating) as rating FROM details WHERE client_id = '$id' GROUP BY rating
I'm trying to get the total of votes for each FANOF_ID (ex: Me). The problem is that a FAN can vote each day for the same FANOF_ID (ex: David Bowie RIP)
So each day I could vote for David Bowie as my favorite singer
ID CREATED FAN_ID FANOF_ID
15 2016-01-24 3 3
16 2016-01-25 3 3
17 2016-01-25 2 3
So from that example I should get a result of 2 fans for 'total' for FANOF_ID (3)
This is my actual SQL
SELECT
distinct `fans_fanofvote`.`fan_id`,
COUNT(`fans_fanofvote`.`fanof_id`) AS `total`
FROM `fans_fanofvote`
GROUP BY `fans_fanofvote`.`fanof_id`
ORDER BY `total` DESC
But it returns 3 records even if I use distinct on fan_id it wont work. How can I get mySQL to do a distinct on FAN_ID
My SQL should return one record like that:
FANOF_ID TOTAL
3 2
You want COUNT(DISTINCT). However, you have to be careful about what you are counting (fan_id) and what you are aggregating by (fanof_id):
SELECT fov.fanof_id,
COUNT(DISTINCT fov.fan_id) AS total
FROM fans_fanofvote fov
GROUP BY fov.fanof_id
ORDER BY total DESC;
Note that table aliases make the query easier to read. And don't use back tick unless really needed.
You didn't mention expected output earlier so it was confusing.
SELECT
`fans_fanofvote`.`fanof_id`,
COUNT(`fans_fanofvote`.`fan_id`) AS `total`
FROM `fans_fanofvote`
GROUP BY `fans_fanofvote`.`fanof_id`
ORDER BY `total` DESC
Use count(distinct )
SELECT
COUNT(distinct `fans_fanofvote`.`fanof_id`) AS `total`
FROM `fans_fanofvote`
GROUP BY `fans_fanofvote`.`fanof_id`
ORDER BY `tot
I have a query which actually have a sorting using order by clause. i have a table like following...
user_id user_name user_age user_state user_points
1 Rakul 30 CA 56
2 Naydee 29 NY 144
3 Jeet 40 NJ 43
.....
i have following query...
select * from users where user_state = 'NY' order by user_points desc limit 50;
This gives me the list of 50 people with most points. I wanted to give least preference to few people who's id's were known. Incase if i do not have enough 50 records then those id's should come in the last in the list. I do not want the users 2 and 3 to come on top of the list even though they have higher points... those people should come on the last of the list from the query. Is there any way to push specific records to last on result set irrespective of query sorting ?
If you want to move specific records (like user_id = 2 and 3) down to the list; Then you can run below Query:
mysql> select *,IF(user_id=2 or user_id=3,0,1) as list_order from users where user_state = 'NY' order by list_order desc, user_points desc limit 50;
select * from (
select *
from users
where user_state = 'NY'
-- this order by ensures that 2 and 3 are included
order by case when user_id in (2,3) then 1 else 2 end, user_points desc
limit 50
) as top48plus2n3
-- this order by ensures that 2 and 3 are last
order by case when user_id in (2,3) then 2 else 1 end, user_points desc
Edit: changed id by user_id and corrected outside order by (sorry about that)
On the inner select:
By using this case calculation, what you do is ensuring that records with ids equal to 2 and 3 are "important" (firstly ordered in the order by). Those receive 1 while the others receive 2 as order value, only after that points are relevant.
On the outer select:
Records with ids 2 and 3 recieve 2 as order value, while the rest recieve 1. So they go last irrespective of its "default"
Here you have a reduced fiddle http://sqlfiddle.com/#!9/377c1/1
I'm trying to determine the score of an entry by finding the difference between the number of upvotes and downvotes it has received in MYSQL by running SELECT (SELECT COUNT(vote_id) AS vote_up FROMvotesWHERE vote='UP'),(SELECT COUNT(vote_id) AS vote_down FROMvotesWHERE vote='DOWN'),(vote_up - vote_down AS vote_score). When I try to run this though, it tells me that I do not have proper syntax. What am I doing wrong?
Also, is there a better way to write this?
And finally, what is the ideal way to find the item with the highest and lowest number of votes? Would I just ORDER BY [above query]?
You can do it with
SELECT some_id
, SUM(
CASE
WHEN vote = 'UP'
THEN 1
WHEN vote = 'DOWN'
THEN -1
ELSE 0
END
) as vote_score
FROM votes
GROUP BY some_id
Note that the better approach is to have +1 or -1 stored in vote, then you can just do:
SELECT some_id, SUM(vote) as vote_score
FROM votes
GROUP BY some_id
BTW if my formatting looks odd to you, I explained it in http://bentilly.blogspot.com/2011/02/sql-formatting-style.html.
You can do it by pulling that last clause into a (SELECT ...) block as well:
SELECT
(SELECT COUNT(vote_id) FROM votes WHERE vote='UP') AS vote_up,
(SELECT COUNT(vote_id) FROM votes WHERE vote='DOWN') AS vote_down,
(SELECT vote_up - vote_down) AS vote_score
ORDER BY vote_whatever;
Note btilly's answer about having +/- 1 be the upvote / downvote representation. It makes a lot more sense in this context, and allows for smaller tables, faster comparisons, and use of the SUM() function when necessary:
SELECT SUM(vote) from votes;
Also note: You'll only get vote_up and vote_down counts using the multiple (SELECT ...) method - SUM(CASE) will only give you the total.
Following up on btilly's answer, If you need to know the lowest and highest but do not need to know what ID has the highest/lowest:
SELECT MIN(score), MAX(score)
FROM (
SELECT SUM(IF(vote = 'DOWN', -1, vote = 'UP')) AS score
FROM votes
GROUP BY ID
)
If you do need to know the ID, use the inner query (add the ID to the select) with a ORDER BY score LIMIT 1 to get the lowest and ORDER BY score DESC LIMIT 1 to get the highest.
note in the case of ties, this will choose only 1 of them.