MySQL count occurrences but return all rows - mysql

Im trying to count occurrences of name, but i want each row returned no matter if that name has already been counted. The data looks like;
ID | NAME
1 Peter
2 Simone
3 Otto
4 Cedric
5 Peter
6 Cedric
7 Cedric
The following only returns one row per unique name
select id, first_name, count(first_name)from table group by first_name
ID | FIRST_NAME | count(first_name)
1 Peter 2
2 Simone 1
3 Otto 1
4 Cedric 3
But im trying to return every row, something like
ID | FIRST_NAME | count(first_name)
1 Peter 2
2 Simone 1
3 Otto 1
4 Cedric 3
5 Peter 2
6 Cedric 3
7 Cedric 3

If you are using MySQL version >= 8.0, then you can use window functions:
select id,
first_name,
count(*) over (partition by first_name)
from table
For earlier versions:
select id,
first_name,
(select count(*) from table where first_name = t.first_name)
from table t

You can use a Correlated subquery:
SELECT t1.id,
t1.first_name,
(SELECT COUNT(id)
FROM table t2
WHERE t2.first_name = t1.first_name) AS total_count
FROM table t1

Edit: now that I've seen the other answers, why is joining better than using a correlated subquery? Because a correlated subquery is executed for every row in your table. When you join it, the query is executed just once.
Then you have to join those queries.
select * from
table
inner join (
select first_name, count(first_name) as name_count from table group by first_name
) qcounts on table.first_name = qcounts.first_name
Also note, that in your query you have to remove id from the select clause, since you neither have it in your group by clause nor do you apply an aggregate function on it. Therefore a random row is returned for this column.
It's a good idea to let MySQL remind you of that by activating the only_full_group_by sql mode. To do this you can do
set global sql_mode = concat(##global.sql_mode, 'only_full_group_by');

Related

selecting multiple max values

i have a table like this on a mysql database:
id | item
-----------
1 | 2
2 | 2
3 | 4
4 | 5
5 | 8
6 | 8
7 | 8
i want the result to be 3 record with the highest Item value
select max(item) returns only 1 value
how can i select multiple max values?
thank you
You can use a derived table to get the maximum value and join it back to the original table to see all rows corresponding to it.
select t.id, t.item
from tablename t
join (select max(item) as mxitem from tablename) x
on x.mxitem = t.item
Edit:
select t.co_travelers_id, t.booking_id, t.accounts_id
from a_co_travelers t
join (select accounts_id, max(booking_id) as mxitem
from a_co_travelers
group by accounts_id) x
on x.mxitem = t.booking_id and t.accounts_id = x.accounts_id
If you use an 'aggregate function' without GROUP BY only one row will be returned.
You may use GROUP BY , with aggregate functions.
Here is SQLFiddle Demo
SELECT id,max(item) AS item
FROM table_name
GROUP BY id
ORDER BY item DESC
LIMIT 3
Hope this helps.
There is the graphical explanation.
There is script mysql (low abstraction level, no inner join or sth)
select * from ocena, uczen where ocena.ocena = (SELECT MAX(ocena.ocena) FROM ocena WHERE ocena.przedmiot_id="4" and ocena.uczen_id="1") and ocena.uczen_id=uczen.id and ocena.przedmiot_id="4" and uczen_id="1"

MySQL compare, count and order by

I am having trouble with understanding how to solve a seemingly simple problem of sorting results.
I want to compare how many other users like the same fruits as like the user with ID 1, a count who has the most matches and display the results in descending order.
users:
1 jack
2 john
3 jim
fruits:
id, title
1 apple
2 banana
3 orange
4 pear
5 mango
relations: 2 indexes (user_id, fruit_id) and (fruit_id, user_id)
user_id, fruit_id
1 1
1 2
1 5
2 1
2 2
2 4
3 3
3 1
expected results: (comparing with Jack's favourite fruits (user_id=1))
user_id, count
1 3
2 2
3 1
Query:
SELECT user_id, COUNT(*) AS count FROM relations
WHERE fruit_id IN (SELECT fruit_id FROM relations WHERE user_id=1)
GROUP BY user_id
HAVING count>=2
More "optimized" query:
SELECT user_id, COUNT(*) AS count FROM relations r
WHERE EXISTS (SELECT 1 FROM relations WHERE user_id=1 and r.fruit_id=fruit_id)
GROUP BY user_id
HAVING count>=2
2 is the minimum number of matches. (required for the future)
explain:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY r index NULL uid 8 NULL 15 Using where; Using index
2 DEPENDENT SUBQUERY relations eq_ref xox,uid xox 8 r.relations,const 1 Using where; Using index
Everything is working fine, until I try to use ORDER BY count DESC
Then I see: Using temporary; Using filesort
I don't want to use temporary tables or filesort. Because in the future, the database should be under high load.
I know, this is how SQL is defined and how it operates. But I can not figure out how to do it in other way? Without temporary tables and filesort.
I need to show the users who has the most matches first.
Please, help me out.
UPDATE:
I did some tests with the query from Walker Farrow (which is still uses a filesort).
20,000 rows - avg 0.05 seconds
120,000 0.20 sec.
1,100,000 2.9 sec.
disappointing results.
It would be possible to change the tables structure, but, with such a counting and sorting - I don't know how.
Is there any suggestions on how this can be done?
Probably the best way to do this would be to create a subquery and then order by in the outer-query, something like this:
select *
from (
SELECT user_id, COUNT(*) AS count FROM relations r
WHERE EXISTS (SELECT 1 FROM relations WHERE user_id=1 and r.fruit_id=fruit_id)
GROUP BY user_id
HAVING count(*)>=2
) x
order by count desc
Also, I don't know why you need to add exists. Can you just say the following:
select *
from (
SELECT user_id, COUNT(*) AS count FROM relations r
WHERE user_id=1
GROUP BY user_id
HAVING count(*)>=2
) x
order by count desc
?
I am not sure, maybe I am missing something. HOpe that helps!

MYSQL select from table where field is in the field

UPDATED
id | id_list
1 | 2,3,5,7
2 | 1,4,5,6
3 | 1,4,6,7
4 | 2,3,5,8
5 | 1,2,4,8
6 | 2,3,7,8
7 | 1,3,6,9
8 | 4,5,6,9
9 | 7,8
let's say I'm up to the content of id=1
I wanted to select all the rows where id is in id_list of id=1 PLUS the row where id=1
so the result would be
rows with id = 1,2,3,5,7
How to do this query guys?
You can also use a self join
Using IN()
select * from atable a
join atable b on (a.id = b.id )
where 1 IN (a.id_list) or b.id =1
Fiddle with IN()
Using FIND_IN_SET()
select * from atable a
join atable b on (a.id = b.id )
where FIND_IN_SET('1', a.id_list) or b.id =1
Fiddle with FIND_IN_SET()
Using UNION ALL
select * from atable
where id =1
UNION ALL
select * from atable
where 1 IN (id_list)
Fiddle with UNION ALL
Your database design is broken; id_list should be represented as a join table instead of as a string. However, to solve your immediate problem:
select * from table where id=1
or id_list like '1%'
or id_list like '%,1,%'
or id_list like '%,1'
Adjust as needed for PreparedStatement. You have to provide all three cases because if you just did
or id_list like '%1%'
and id_list contained the value 11, you'd get an incorrect match
Try this (see SQL-Fiddle):
SELECT * FROM tbl
WHERE id = 1
OR INSTR((SELECT id_list FROM tbl WHERE id = '1'), id) > 0;
Tested with MySQL 5.5.30
try this one
select *
from tbl
where id=1
or id_list like '%1%'
This appears to call for a union of two sets. The one set would be the single row whose id matches the specified value:
SELECT
id
FROM
atable
WHERE
id = #id
The other set would be the result of this self-join:
SELECT
item.id
FROM
atable AS item
INNER JOIN
atable AS list
ON
FIND_IN_SET(item.id, list.id_list)
WHERE
list.id = #id
That is, the row with the specified id is matched against every row in the table on the condition that the other row's id is found in the specified row's id_list.
You can try the complete query at SQL Fiddle.
Please note that lists aren't a very good design feature. In your situation, it might be better to use a many-to-many table as suggested by #Othman. Only I would probably use a slightly different query to get the desired output, because his doesn't include the specified row itself:
SELECT
id
FROM
manytomany
WHERE
id = #id
UNION
SELECT
linked_id
FROM
manytomany
WHERE
id = #id
;
While the entries in manytomany are assumed to be unique, the query uses the UNION DISTINCT operator because of the potential duplicates returned by the first subquery, although it is possible to move the application of DISTINCT to the first subquery only like this:
SELECT DISTINCT
id
FROM
manytomany
WHERE
id = #id
UNION ALL
SELECT
linked_id
FROM
manytomany
WHERE
id = #id
;
That first subquery could actually be rewritten simply as SELECT #id AS id, but the rewrite would only make sense if the passed value was guaranteed to be valid, i.e. that it would definitely be found in manytomany.id.
Here's a demo for the other approach too (all three variations, including the SELECT #id AS id one).
I don't have an answer for your question but I encourage you to redesign your table like this I think this called many to many relation
id | friend
1 | 2
1 | 3
1 | 5
1 | 7
2 | 1
2 | 4
2 | 5
2 | 6
3 | 1
3 | 4
3 | 6
3 | 7
And then your query will be like this
SELECT DISTINCT(friend) FROM `new` WHERE id = 1
I am assuming you are using php..
My suggestion is to grab the id_list for id 1.
Explode that id_list on the comma, and then do another mysql query to grab the remaining results for 5|3|6|8
ex) $idarray = explode(",", $result);
select * from your_table where id in ('5','3','6','8')
OPTION 2:
SELECT * FROM your_table
WHERE id = '1'
OR id IN ('\''+(SELECT REPLACE(id_list,',','\',\'') FROM your_table WHERE id = '1')+'\'')
EDIT: Oops, sorry, that should be an OR instead.

how to find duplicate count without counting original

I need to count the number of duplicate emails in a mysql database, but without counting the first one (considered the original). In this table, the query result should be the single value "3" (2 duplicate x#q.com plus 1 duplicate f#q.com).
TABLE
ID | Name | Email
1 | Mike | x#q.com
2 | Peter | p#q.com
3 | Mike | x#q.com
4 | Mike | x#q.com
5 | Frank | f#q.com
6 | Jim | f#q.com
My current query produces not one number, but multiple rows, one per email address regardless of how many duplicates of this email are in the table:
SELECT value, count(lds1.leadid) FROM leads_form_element lds1 LEFT JOIN leads lds2 ON lds1.leadID = lds2.leadID
WHERE lds2.typesID = "31" AND lds1.formElementID = '97'
GROUP BY lds1.value HAVING ( COUNT(lds1.value) > 1 )
It's not one query so I'm not sure if it would work in your case, but you could do one query to select the total number of rows, a second query to select distinct email addresses, and subtract the two. This would give you the total number of duplicates...
select count(*) from someTable;
select count(distinct Email) from someTable;
In fact, I don't know if this will work, but you could try doing it all in one query:
select (count(*)-(count(distinct Email))) from someTable
Like I said, untested, but let me know if it works for you.
Try doing a group by in a sub query and then summing up. Something like:
select sum(tot)
from
(
select email, count(1)-1 as tot
from table
group by email
having count(1) > 1
)

How to group by a field and return list of other field?

I have a table with columns id, user
I want to group by column id and show a list of users (comma separated) for each id.
In the final output I need to display:
user joe - id 1
users jim, mark, john - id 2
user dave - id 3
....
I'm getting error "Cardinality violation: 1242 Subquery returns more than 1 row" if I try this:
SELECT id, (SELECT distinct(user) FROM mytable b where a.id = b.id)
FROM mytable a
GROUP BY id
Since you are using MySQL, there is a built-in function for that which is the GROUP_CONCAT function. For example, you have a records like this:
ID User
1 Joe
2 Jim
2 Mark
2 John
3 Dave
If you try to run this query:
SELECT ID, GROUP_CONCAT(User) Users
FROM tableName
GROUP BY ID
you will then have a result like this:
ID Users
1 Joe
2 Jim, Mark, John
3 Dave
What you're looking for is the GROUP_CONCAT aggregate function:
SELECT id, GROUP_CONCAT(user) FROM table GROUP BY id