MySQL Conditional count based on a value in another column - mysql

I have table that looks like this:
id rank
a 2
a 1
b 4
b 3
c 7
d 1
d 1
e 9
I need to get all the distinct rank values on one column and count of all the unique id's that have reached equal or higher rank than in the first column.
So the result I need would be something like this:
rank count
1 5
2 4
3 3
4 3
7 2
9 1
I've been able to make a table with all the unique id's with their max rank:
SELECT
MAX(rank) AS 'TopRank',
id
FROM myTable
GROUP BY id
I'm also able to get all the distinct rank values and count how many id's have reached exactly that rank:
SELECT
DISTINCT TopRank AS 'rank',
COUNT(id) AS 'count of id'
FROM
(SELECT
MAX(rank) AS 'TopRank',
id
FROM myTable
GROUP BY id) tableDerp
GROUP BY TopRank
ORDER BY TopRank ASC
But I don't know how to get count of id's where the rank is equal OR HIGHER than the rank in column 1. Trying SUM(CASE WHEN TopRank > TopRank THEN 1 END) naturally gives me nothing. So how can I get the count of id's where the TopRank is higher or equal to each distinct rank value? Or am I looking in the wrong way and should try something like running totals instead? I tried to look for similar questions but I think I'm completely on a wrong trail here since I couldn't find any and this seems a pretty simple problem that I'm just overthinking somehow. Any help much appreciated.

One approach is to use a correlated subquery. Just get the list of ranks and then use a correlated subquery to get the count you are looking for:
SELECT r.rank,
(SELECT COUNT(DISTINCT t2.id)
FROM myTable t2
WHERE t2.rank >= r.rank
) as cnt
FROM (SELECT DISTINCT rank FROM myTable) r;

Related

MYSQL - Displaying rows with max count values in a group with other columns

I have been searching at lot and have found topics like similar topics but none were exactly the solutions that I was looking for. In this case, I have a working code, but something that seems extremely hacky to me and should have a simpler and better way of getting it done.
The "Test" table
id
--
1
1
1
2
2
2
3
4
4
Nothing complex, just a simple table with some repeating id values
What I want is to group these id together and show all the id that are repeating the most i.e
id | count
----------
1 3
2 3
The solution that I have currently come up with
select
#max := max(count) as count
from (
select
id,
count(id) as count
from
test
group by
id
)
as
inner_table;
select
id, count
from (
select
id,
count(id) as count
from
test
group by
id
)
as
inner_table
where count = #max;
One way to do it with group by and having.
select id,count(*) as cnt
from t
group by id
having count(*)=(select count(*) as cnt
from t
group by id
order by cnt desc
limit 1)

How to GROUP BY 2 different columns together

I have 2 columns having users id participating in a transaction, source_id and destination_id. I'm building a function to sum all transactions grouped by any user participating on it, either as source or as destination.
The problem is, when I do:
select count (*) from transactions group by source_id, destination_id
it will first group by source, then by destination, I want to group them together. Is it possible using only SQL?
Sample Data
source_user_id destination_user_id
1 4
3 4
4 1
3 2
Desired result:
Id Count
4 - 3 (4 appears 3 times in any of the columns)
3 - 2 (3 appears 2 times in any of the columns)
1 - 2 (1 appear 2 times in any of the columns)
2 - 1 (1 appear 1 time in any of the columns)
As you can see on the example result, I want to know the number of times an id will appear in any of the 2 fields.
Use union all to get the id's into one column and get the counts.
select id,count(*)
from (select source_id as id from tbl
union all
select destination_id from tbl
) t
group by id
order by count(*) desc,id
edited to add: Thank you for clarifying your question. The following isn't what you need.
Sounds like you want to use the concatenate function.
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_concat
GROUP BY CONCAT(source_id,"_",destination_id)
The underscore is intended to distinguish "source_id=1, destination_id=11" from "source_id=11, destination_id=1". (We want them to be 1_11 and 11_1 respectively.) If you expect these IDs to contain underscores, you'd have to handle this differently, but I assume they're integers.
It may look like this.
Select id, count(total ) from
(select source_id as id, count (destination_user_id) as total from transactions group by source_id
union
select destination_user_id as id , count (source_id) as total from transactions group by destination_user_id ) q group by id

SQL query for selecting maximum from 2 different columns

I got a question in my homework for SQL about selecting the maximum values from the same table that have different class "Letters"
For example:
ID Student Group Avg(value)
-------------------------------------
1 stud1 A 9
2 stud2 A 9.5
3 stud3 B 8
4 stud4 B 8.5
What my query should do, is to show stud2 and stud4.The maximum from their respective groups.
I managed to do it in the end, but it took a lot of characters so I thought that maybe there's a shorter way to do. Any ideas? I used to first search the id or the stud that has max avg(value) from group A, intersecting with the id of the stud that has max avg(value) from B and then putting everything into one big select and then using those intersected IDs into another query that requested to show some different things about those IDs. But as I said, it looked far too long and thought that maybe there's an shorter way.
Try this (I renamed group to grp and avg to avg_val as those are reserved keywords):
select t1.*
from your_table t1
inner join (
select grp, max(avg_val) avg_val
from your_table
group by grp
) t2 on t1.grp = t2.grp
and t1.avg_val = t2.avg_val;
It finds maximum avg value per group and joins it with original table to get the corresponding students.
Please note that if there are multiple students with same avg as the max value of the that group, all of those students will be returned.

Adding Row Values when there are no results - MySQL

Problem Statement: I need my result set to include records that would not naturally return because they are NULL.
I'm going to put some simplified code here since my code seems to be too long.
Table Scores has Company_type, Company, Score, Project_ID
Select Score, Count(Project_ID)
FROM Scores
WHERE company_type= :company_type
GROUP BY Score
Results in the following:
Score Projects
5 95
4 94
3 215
2 51
1 155
Everything is working fine until I apply a condition to company_type that does not include results in one of the 5 score categories. When this happens, I don't have 5 rows in my result set any more.
It displays like this:
Score Projects
5 5
3 6
1 3
I'd like it to display like this:
Score Projects
5 5
4 0
3 6
2 0
1 3
I need the results to always display 5 rows. (Scores = 1-5)
I tried one of the approaches below by Spencer7593. My simplified query now looks like this:
SELECT i.score AS Score, IFNULL(count(*), 0) AS Projects
FROM (SELECT 5 AS score
UNION ALL
SELECT 4
UNION ALL
SELECT 3
UNION ALL
SELECT 2
UNION ALL
SELECT 1) i
LEFT JOIN Scores ON Scores.score = i.score
GROUP BY Score
ORDER BY i.score DESC
And gives the following results, which is accurate except that the rows with 1 in Projects should actually be 0 because they are derived by the "i". There are no projects with a score of 5 or 2.
Score Projects
5 1
4 5
3 6
2 1
1 3
Solved! I just needed to adjust my count to specifically look at the project count - count(project) rather than count(*). This returned the expected results.
If you always want your query to return 5 rows, with Score values of 5,4,3,2,1... you'll need a rowsource that supplies those Score values.
One approach would be to use a simple query to return those fixed values, e.g.
SELECT 5 AS score
UNION ALL SELECT 4
UNION ALL SELECT 3
UNION ALL SELECT 2
UNION ALL SELECT 1
Then use that query as inline view, and do an outer join operation to the results from your current query
SELECT i.score AS `Score`
, IFNULL(q.projects,0) AS `Projects`
FROM ( SELECT 5 AS score
UNION ALL SELECT 4
UNION ALL SELECT 3
UNION ALL SELECT 2
UNION ALL SELECT 1
) i
LEFT
JOIN (
-- the current query with "missing" Score rows goes here
-- for completeness of this example, without the query
-- we emulate that result with a different query
SELECT 5 AS score, 95 AS projects
UNION ALL SELECT 3, 215
UNION ALL SELECT 1, 155
) q
ON q.score = i.score
ORDER BY i.score DESC
It doesn't have to be the view query in this example. But there does need to be a rowsource that the rows can be returned from. You could, for example, have a simple table that contains those five rows, with those five score values.
This is just an example approach for the general approach. It might be possible to modify your existing query to return the rows you want. But without seeing the query, the schema, and example data, we can't tell.
FOLLOWUP
Based on the edit to the question, showing an example of the current query.
If we are guaranteed that the five values of Score will always appear in the Scores table, we could do conditional aggregation, writing a query like this:
SELECT s.score
, COUNT(IF(s.company_type = :company_type,s.project_id,NULL)) AS projects
FROM Scores s
GROUP BY s.score
ORDER BY s.score DESC
Note that this will require a scan of all the rows, so it may not perform as well. The "trick" is the IF function, which returns a NULL value in place of project_id, when the row would have been excluded by the WHERE clause.)
If we are guaranteed that project_id is non-NULL, we could use a more terse MySQL shorthand expression to achieve an equivalent result...
, IFNULL(SUM(s.company_type = :company_type),0) AS projects
This works because MySQL returns 1 when the comparison is TRUE, and otherwisee returns 0 or NULL.
Try something like this:
select distinct score
from (
select distinct score from scores
) s
left outer join (
Select Score, Count(Project_ID) cnt
FROM Scores
WHERE company_type= :company_type
) x
on s.score = x.score
Your posted query would not work without a group by statement. However, even there, if you don't have those particular scores for that company type, it wouldn't work either.
One option is to use an outer join. That would require a little more work though.
Here's another option using conditional aggregation:
select Score, sum(company_type=:company_type)
from Scores
group by Score

MYSQL: Count the number of times a specific integer appears in a column then making a count of how many of these are present?

I have a table with figures like this
Report used UserID
1 2
1 2
1 2
2 2
In this case I'm looking to count the 1's in the 'Report used' column, which would give me the value 3. I might find a few of these in this column for different users, so I'd want to count how many times I found 3 1's.
I've tried using SELECT COUNT to count specific numbers but I'm not sure how to count this count, if you follow me.
Try this:
SELECT userid, COUNT(reportused) onescount
FROM tablename
WHERE reportused = 1
GROUP BY userid
Also check this:
SELECT COUNT(userid)
FROM (SELECT userid, COUNT(reportused) onescount
FROM tablename
WHERE reportused = 1
GROUP BY userid) a
WHERE onescount = 3
If I've got it right:
select Report_used,RU_count,count(*)
from
(select Report_used, UserID, count(*) RU_Count
from t
group by Report_used, UserID) t1
group by Report_used,RU_count;