MySQL: Count occurrences of distinct values for each row - mysql

Based on an example already given, I would like to ask my further question.
MySQL: Count occurrences of distinct values
example db
id name
----- ------
1 Mark
2 Mike
3 Paul
4 Mike
5 Mike
6 John
7 Mark
expected result
name count
----- -----
Mark 2
Mike 3
Paul 1
Mike 3
Mike 3
John 1
Mark 2
In my opinion 'GROUP BY' doesn't help.
Thank you very much.

Simplest approach would be using Count() as Window Function over a partition of name; but they are available only in MySQL 8.0.2 and onwards.
However, another approach is possible using a Derived Table. In a sub-select query (Derived Table), we will identify the counts for each unique name. Now, we simply need to join this to the main table, to show counts against each name (while not doing a grouping on them):
SELECT
t1.name,
dt.total_count
FROM your_table AS t1
JOIN
(
SELECT name,
COUNT(*) AS total_count
FROM your_table
GROUP BY name
) AS dt ON dt.name = t1.name
ORDER BY t1.id
If MySQL 8.0.2+ is available, the solution would be less verbose:
SELECT
name,
COUNT(*) OVER (PARTITION BY name) AS total_count
FROM your_table

Related

Can I compare different rows in the same MySQL table (SQL)

Is there a way to compare different rows in the same MySQL table? I want to count how many users have changed their names.
Here is roughly what I got in the MySQL log table...
id userid name
1 1 Joe
2 1 Joe
3 1 Joe
4 2 Fiona
5 3 Mark
6 3 Marcel
7 3 Marcel
8 4 Mary
9 4 Marie
You can see that Joe's name has not changed even though he is in the log table 3 times. "Mark" was changed to "Marcel" and "Mary" was changed to "Marie". So if this was the whole of the table, I want to know that 2 people have changed their names (out of four).
SELECT userid, GROUP_CONCAT(name) FROM tablename GROUP BY userid ORDER BY userid DESC
I can examine the output with a scripting language (PHP), I'm just wondering if there is something I can do in SQL that would be neater and only give me the names that are changed.
I'm not sure if this is possible. Haven't found a solution to this yet, if it's a duplicate please let me know. Thanks.
How about using a HAVING clause?
SELECT userid
FROM tablename
GROUP BY userid
HAVING MIN(name) <> MAX(name)
ORDER BY userid DESC;
If you want the names without duplicates:
SELECT userid, GROUP_CONCAT(DISTINCT name)
FROM tablename
GROUP BY userid
HAVING MIN(name) <> MAX(name)
ORDER BY userid DESC;

Guidance required for sql query

I have a database with one table as shown below. Here I'm trying to write a query to display the names of medication manufactured by the company that manufactures the most number of medications.
By looking at the table we could say the medication names which belongs to the company id 1 and 2 - because those company manufactures the most medication according to this table, but I'm not sure how to write a query for selecting the same i said before.
ID | COMPANY_ID | MEDICATION_NAME
1 1 ASPIRIN
2 1 GLUCERNA
3 2 SIBUTRAMINE
4 1 IBUPROFEN
5 2 VENOFER
6 2 AVONEN
7 4 ACETAMINOPHEN
8 3 ACETAMINO
9 3 GLIPIZIDE
Please share your suggestions. Thanks!
Several ways to do this. Here's one which first uses a subquery to get the maximum count, then another subquery to get the companies with that count, and finally the outer query to return the results:
select *
from yourtable
where companyid in (
select companyid
from yourtable
group by companyid
having count(1) = (
select count(1) cnt
from yourtable
group by companyid
order by 1 desc
limit 1
)
)
SQL Fiddle Demo
This Query might work. I have not tested but the logic is correct
SELECT MEDICATION_NAME
FROM TABLE where
COMPANY_ID=(SELECT
MAX(counted)
FROM ( SELECT COUNT(*) AS counted FROM TABLE ) AS counts);

How to apply an ORDER and LIMIT to each sub-query in an IN() clause?

Need to select rows using multiple WHERE params but only return the latest row for each condition; prefer to hit DB with one SQL statement rather than use a loop.
Table
Dummy data:
id first last
------------------------
1 jeff jones
2 homer simpson
3 john doe
4 jeff jones
5 jeff jones
6 sam smith
7 homer simpson
8 john doe
SQL
My current sql statement:
SELECT * FROM members
WHERE (first,last) IN (('jeff','jones'), ('homer','simpson'))
ORDER BY id DESC LIMIT 1
Results needed
Return one row from each sub-query showing the latest entries, i.e. highest id:
id first last
------------------------
5 jeff jones
7 homer simpson
Thanks in advance.
By grouping with first, last you get one result per combination:
SELECT MAX(id), first, last
FROM members
WHERE (first,last) IN (('jeff','jones'), ('homer','simpson'))
GROUP BY first, last
ORDER BY id;
MAX(id) selects the maximum id per first/last combination. Order by id afterwards.
Check this Fiddle demo.
p.s.: added as per comment. Note: you cannot use LIMIT in a sub query. So use a JOIN:
SELECT sup.id, sup.age, sup.first, sup.last
FROM members sup
JOIN ((SELECT id, age, first, last -- choose first two Homers
FROM members
WHERE (first,last) IN (('homer','simpson'))
ORDER BY id DESC
LIMIT 2)
UNION
(SELECT id, age, first, last -- unify with first two Jeffs
FROM members
WHERE (first,last) IN (('jeff','jones'))
ORDER BY id DESC
LIMIT 2)) sub -- left join
WHERE sup.id = sub.id -- and select only entries from sub
AND sup.age = sub.age
AND sup.first = sub.first
AND sup.last = sub.last;
Check this Fiddle here.
Use GROUP BY
SELECT MAX(id), first, last
FROM members
WHERE (first,last) IN (('jeff','jones'), ('homer','simpson'))
GROUP BY first, last
ORDER BY id DESC
SELECT *
FROM members m1
WHERE (first,last) IN (('jeff','jones'), ('homer','simpson')) AND
id = (SELECT MAX(id)
FROM members m2
WHERE m1.first = m2.first AND m1.last = m2.last)
SQL Fiddle Demo

Select distinct column along with some other columns in MySQL

I can't seem to find a suitable solution for the following (probably an age old) problem so hoping someone can shed some light. I need to return 1 distinct column along with other non distinct columns in mySQL.
I have the following table in mySQL:
id name destination rating country
----------------------------------------------------
1 James Barbados 5 WI
2 Andrew Antigua 6 WI
3 James Barbados 3 WI
4 Declan Trinidad 2 WI
5 Steve Barbados 4 WI
6 Declan Trinidad 3 WI
I would like SQL statement to return the DISTINCT name along with the destination, rating based on country.
id name destination rating country
----------------------------------------------------
1 James Barbados 5 WI
2 Andrew Antigua 6 WI
4 Declan Trinidad 2 WI
5 Steve Barbados 4 WI
As you can see, James and Declan have different ratings, but the same name, so they are returned only once.
The following query returns all rows because the ratings are different. Is there anyway I can return the above result set?
SELECT (distinct name), destination, rating
FROM table
WHERE country = 'WI'
ORDER BY id
Using a subquery, you can get the highest id for each name, then select the rest of the rows based on that:
SELECT * FROM table
WHERE id IN (
SELECT MAX(id) FROM table GROUP BY name
)
If you'd prefer, use MIN(id) to get the first record for each name instead of the last.
It can also be done with an INNER JOIN against the subquery. For this purpose the performance should be similar, and sometimes you need to join on two columns from the subquery.
SELECT
table.*
FROM
table
INNER JOIN (
SELECT MAX(id) AS id FROM table GROUP BY name
) maxid ON table.id = maxid.id
The problem is that distinct works across the entire return set and not just the first field. Otherwise MySQL wouldn't know what record to return. So, you want to have some sort of group function on rating, whether MAX, MIN, GROUP_CONCAT, AVG, or several other functions.
Michael has already posted a good answer, so I'm not going to re-write the query.
I agree with #rcdmk . Using a DEPENDENT subquery can kill performance, GROUP BY seems more suitable provided that you have already INDEXed the country field and only a few rows will reach the server. Rewriting the query giben by #rcdmk , I added the ORDER BY NULL clause to suppress the implicit ordering by GROUP BY, to make it a little faster:
SELECT MIN(id) as id, name, destination as rating, country
FROM table WHERE country = 'WI'
GROUP BY name, destination ORDER BY NULL
You can do a GROUP BY clause:
SELECT MIN(id) AS id, name, destination, AVG(rating) AS rating, country
FROM TABLE_NAME
GROUP BY name, destination, country
This query would perform better in large datasets than the subquery alternatives and it can be easier to read as well.

SQL Grouping and counting using existing row

Lets say we have a table Requests with structure:
user_id
product_id
count
With entries like:
1 1 5
1 2 6
1 1 3
2 1 7
2 1 3
2 2 5
I want to count how much of each product each user has.
And get output like this:
1 1 8
1 2 6
2 1 10
2 2 5
Use GROUP BY with the SUM aggregate function:
SELECT user_id, product_id, SUM(count) AS total
FROM Requests
GROUP BY user_id, product_id
Your query will look something like this:
SELECT user_id, product_id, SUM(`count`)
FROM Requests
GROUP BY user_id, product_id
I wouldn't name a field "count" if I had the choice, as it's a SQL function and could cause weird naming conflicts down the road.
You can find a tutorial on how to use GROUP BY here:
SQL GROUP BY statement
You can also find specific information on the syntax and use of GROUP BY in MySQL in the MySQL Manual:
MySQL SELECT Syntax
MySQL GROUP BY Functions And Modifiers