How to get total occurrence of a value within its own query? - mysql

Let's say we have this query
SELECT * FROM table
And this result from it.
id | user_id
------------
1 | 1
------------
2 | 1
------------
3 | 2
------------
4 | 1
How could I get the count of how often a user_id appears as another field (without some major SQL query)
id | user_id | count
--------------------
1 | 1 | 3
--------------------
2 | 1 | 3
--------------------
3 | 2 | 1
--------------------
4 | 1 | 3
We have this value currently in code, but we are implementing sorting to this table and I would like to be able to sort in the SQL query.
BTW if this is not possible without some major trick, we are just going to skip sorting on that field.

You'll just want to add a subquery on the end, I believe:
SELECT
t.id,
t.user_id,
(SELECT COUNT(*) FROM table WHERE user_id = t.user_id) AS `count`
FROM table t;

SELECT o.id, o.user_id, (
SELECT COUNT(id)
FROM table i
WHERE i.user_id = o.user_id
GROUP BY i.user_id
) AS `count`
FROM table o
I suspect this query as not being a performance monster but it should work.

Related

MySQL add avg of count by id to existing select with id

Im not even sure what the title of this question should be but lets start out with my data.
I have a table of users who have taken a few lessons while belonging to a particular training center.
lesson table
id | lesson_id | user_id | has_completed
----------------------------------------
1 | asdf3314 | 2 | 1
2 | d13saf12 | 2 | 1
3 | a33adff5 | 2 | 0
4 | a33adff5 | 1 | 1
5 | d13saf12 | 1 | 0
user table
id | center_id | ...
----------------------------------------
1 | 20 | ...
2 | 30 | ...
training center table
id | center_name | ...
----------------------------------------
20 | learn.co | ...
30 | teach.co | ...
I've written a small chunk but am now stuck as I don't know how to proceed. This statement gets the counted total of completed lessons per user. it then figures the average completed value from a center id. if two users belong to a center and have completed 3 lessons and 2 lessons it finds the average of 3 and 2 then returns that.
SELECT
FLOOR(AVG(a.total)) AS avg_completion,
FROM
(SELECT
user_id,
user.center_id,
count(user_id) AS total
FROM lesson
LEFT JOIN user ON user.id = user_id
WHERE is_completed = 1 AND center_id = 2
GROUP BY user_id) AS a;
The question I have is how do I loop through the training centers table and also append average data from similar select statement as above to each center that is queried. I cant seem to pass the center id down to the subquery so there must be a fundamentally different way to achieve the same query but also loop through training centers.
An example of desired result:
center.id | avg_completion | ...training center table
-----------------------------------------------------
20 | 2 | ...
Your main query needs to select a.center_id and then use GROUP BY center_id. You can then join it with the training_center table.
SELECT c.*, x.avg_completion
FROM training_center AS c
JOIN (
SELECT
a.center_id,
FLOOR(AVG(a.total)) AS avg_completion
FROM (
SELECT
user_id
user.center_id,
count(*) AS total
FROM lesson
JOIN user ON user.id = user_id
WHERE is_completed = 1 AND center_id = 2
GROUP BY user_id) AS a
GROUP BY a.center_id) AS x
ON x.center_id = c.id
If I understand correctly:
select u.center_id, count(*) as num_users,
sum(l.has_completed) as num_completed,
avg(l.has_completed) as completed_ratio
from lesson l join
user u
on l.user_id = u.id
group by u.center_id

MySQL group by with left join

I am trying to do a very complex query (at least extremely complex for me not for YOU :) )
I have users and comments table.
SQL Fiddle: http://sqlfiddle.com/#!9/b1f845/2
select user_id, status_id from comments where user_id in (2,3);
+---------+-----------+
| user_id | status_id |
+---------+-----------+
| 2 | 10 |
| 2 | 10 |
| 2 | 10 |
| 2 | 7 |
| 2 | 7 |
| 2 | 10 |
| 3 | 9 |
| 2 | 9 |
| 2 | 6 |
+---------+-----------+
If I use
select user_id, status_id from comments where user_id in (2,3)
It returns a lot of duplicate values.
What I want to get if possible.
If you see status_id = 10 has user_id= 2,3 and 4 and 2 multiple times.
So from here I want to get maximum of latest user_id (unique) so for example,
it will be user_id = 4 and 2 now the main complex part. I now want to get users information of user_id= 4 and 2 in one column so that at the end I can get something like this
status_id | userOneUserName | userTwoUserName
10 sadek4 iamsadek2
---------------------------------------------
7 | iamsadek2 | null
---------------------------------------------
9 . | iamsadek2 | sadek2
---------------------------------------------
6 | iamsadek2 | null
How can I achieve such a complex things.
Currently I have to do it using application logic.
Thank you for your time.
I think this might be what you literally want here:
SELECT DISTINCT
status_id,
(SELECT MAX(user_id) FROM comments c2 WHERE c1.status_id = c2.status_id) user_1,
(SELECT user_id FROM comments c2 WHERE c1.status_id = c2.status_id
ORDER BY user_id LIMIT 1 OFFSET 1) user_2
FROM comments c1
WHERE user_id IN (2,3);
Demo (your update Fiddle)
We can use correlated subqueries to find the max user_id and second-to-max user_id for each status_id, and then spin each of those out as two separate columns. Using a GROUP_CONCAT approach might be preferable here, since it would also allow you to easily accommodate any numbers of users as a CSV list.
Also, if you were using MySQL 8+ or greater, then we could take advantage of the rank analytic functions, which would also be easier.
select status_id, GROUP_CONCAT(distinct(user_id) SEPARATOR ',')
from comments
group by status_id
I would suggest using GROUP BY and GROUP_CONCAT, e.g. like so:
SELECT status_id, GROUP_CONCAT(userName) AS users, GROUP_CONCAT(DISTINCT c.user_id) AS user_ids
FROM (
SELECT DISTINCT status_id, user_id FROM comments WHERE user_id in (2,3)
) c
JOIN users u ON (c.user_id = u.id)
GROUP BY status_id
ORDER BY status_id DESC

mysql group by but only group if second row is the same

im wondering what the smartest way is to group my mysql results... I have the following table structure:
- id
- userId
- status (values from 1-100)
Lets say with the following content:
1 | 1 | 10
2 | 1 | 10
3 | 1 | 15
4 | 2 | 15
5 | 3 | 10
Now I want to group all results by user but only for each status. So the results im looking for should be:
1 | 1 | 10
3 | 1 | 15
4 | 2 | 15
5 | 3 | 10
Hope you understand want im looking for...
Best
Tassilo
If you need the id, then a GROUPing query is needed; this will produce the results you shown:
SELECT MIN(id), userId, status
FROM your_table
GROUP BY userId, status
;
If you don't need the id, then GROUPing is not the best tool, use DISTINCT instead; like so:
SELECT DISTINCT userId, status
FROM your_table
;
The topic of this question say "Group only if next row is the same" in that case I would do something like this:
create table USER_(id integer, UserId integer, status integer);
insert into USER_ values(1,1,10);
insert into USER_ values(2,1,10);
insert into USER_ values(3,1,115);
insert into USER_ values(4,2,115);
insert into USER_ values(5,3,10);
insert into USER_ values(6,1,10);
select min(a.id)as id, a.userId, a.status ,count(*) from USER_ a join USER_ b
on a.userid = b.userid and a.id = b.id-1 group by a.userId,a.status;
id | userid | status | count
-----+--------+--------+-------
1 | 1 | 10 | 2
If I look at the explanation for the question here then, I would do something like this:
select min(a.id) as id, a.userId, a.status from USER_ a
group by a.userId,a.status order by a.userid,status;
id | userid | status
----+--------+--------
1 | 1 | 10
3 | 1 | 15
4 | 2 | 15
5 | 3 | 10
Please correct if I have a wrong understanding of the question

mysql: How to exclude rows from table which exist in table_alias with good perfomanse?

I've sql with NOT EXIST and it works very slowly in big db:
SELECT *
FROM
(
SELECT * FROM profiles ORDER BY id DESC
/* I need this order HERE! More info: https://stackoverflow.com/q/43516402/2051938 */
) AS users
WHERE
NOT EXISTS (
SELECT *
FROM request_for_friendship
WHERE
(
request_for_friendship.from_id = 1
AND
request_for_friendship.to_id = users.id
)
OR
(
request_for_friendship.from_id = users.id
AND
request_for_friendship.to_id = 1
)
)
LIMIT 0 , 1;
And I think I need to get request_for_friendship with some WHERE and after that check NOT EXIST, like this:
SELECT users.*
FROM
(
SELECT * FROM profiles ORDER BY id DESC
) AS users,
(
SELECT *
FROM request_for_friendship
WHERE
request_for_friendship.from_id = 1
OR
request_for_friendship.to_id = 1
) AS exclude_table
WHERE
NOT EXISTS
(
SELECT *
FROM exclude_table /* #1146 - Table 'join_test.exclude_table' doesn't exist */
WHERE
request_for_friendship.from_id = users.id
OR
request_for_friendship.to_id = users.id
)
LIMIT 0 , 1;
But it doesn't work: #1146 - Table 'join_test.exclude_table' doesn't exist
My tables:
1) profiles
+----+---------+
| id | name |
+----+---------+
| 1 | WILLIAM |
| 2 | JOHN |
| 3 | ROBERT |
| 4 | MICHAEL |
| 5 | JAMES |
| 6 | DAVID |
| 7 | RICHARD |
| 8 | CHARLES |
| 9 | JOSEPH |
| 10 | THOMAS |
+----+---------+
2) request_for_friendship
+----+---------+-------+
| id | from_id | to_id |
+----+---------+-------+
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 8 |
| 5 | 4 | 1 |
| 6 | 9 | 1 |
+----+---------+-------+
How to do some like this or better for perfomance?
p.s. I need to get only 1 row from table
Demo: http://rextester.com/DTA64368
I've already tried LEFT JOIN, but I've problem with order with him. mysql: how to save ORDER BY after LEFT JOIN without reorder?
First, do not use subqueries unnecessarily. Second, split the NOT EXISTS into two conditions:
SELECT p.*
FROM profiles p
WHERE NOT EXISTS (SELECT 1
FROM request_for_friendship rff
WHERE rff.from_id = 1 AND
rff.to_id = p.id
) AND
NOT EXISTS (SELECT 1
FROM request_for_friendship rff
WHERE rff.to_id = 1 AND
rff.from_id = p.id
)
ORDER BY id DESC;
This can now make use of two indexes: request_for_friendship(to_id, from_id) and request_for_friendship(from_id, to_id). Each index is needed for one of the NOT EXISTS conditions.
I still think there's ways to optimize this as 'in' is generally slower.
SELECT *
FROM profiles p
WHERE NOT EXISTS (SELECT 1
FROM request_for_friendship
WHERE (request_for_friendship.from_id,
request_for_friendship.to_id)
in ((1,p.id),
(p.id,1))
)
Get rid of the id in request_for_friendship. It wastes space and performance. The table has a "natural" PRIMARY KEY, which I will get to in a moment.
Since it seems that the relationship seems to commutative, let's make use of that by sorting the from and to -- put the smaller id in from and the larger is to. See LEAST() and GREATEST() functions.
Then you need only one EXISTS(), not two. And have
PRIMARY KEY(from_id, to_id)
Now to rethink the purpose of the query... You are looking for the highest id that is not "related" to id #1, correct? That sounds like a LEFT JOIN.
SELECT
FROM profiles AS p
LEFT JOIN request_for_friendship AS r ON r.to = p.id AND r.from = 1
WHERE r.to IS NULL
ORDER BY id DESC
LIMIT 1;
This may run about the same speed as the EXISTS -- Both walk through profiles from the highest id, reaching into the other table to see if a row is there.
If there is no such id, then the entire profiles table will be scanned, plus a the same number of probes into the other table.

group mysql count values in comma separated field

I have a users table with columns: user_id, mechanic_id
and
mechanics table with id
I would like to count how many users have the same mechanic.
Users table
+-------------------------+
| user_Id mechanic_id |
+-------------------------+
| 1 1,2 |
| 2 2,1 |
| 3 2,1,8,16 |
| 4 1,16,3 |
+-------------------------+
mechanics table
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
...
Count for $id1 is: 4
Count for $id2 is: 3
Count for $id3 is: 1
Count for $id8 is: 1
Count for $id16 is: 2
Best solution: scrap this table design and rebuild with a properly normalized once. Then a simple join + group by + count query will work.
Worst solution: use MySQL's find_in_set() function:
SELECT mechanics.id, COUNT(user_ID)
FROM mechanics
LEFT JOIN users ON (FIND_IN_SET(mechanics.id, users.mechanic_id) > 0)
GROUP BY mechanics.id
I don't know why I am violating the basic principles of database normalization...Each user has usually one mechanic or max 2 or 3, so that's why I decided to store data in users table.
I found solution based on #Marc B:
SELECT count(*) FROM users a
INNER JOIN mechanics b
ON (FIND_IN_SET(b.id, a.mechanic_id) > 0)
WHERE b.id = '{$id}'
group by b.id
SELECT COUNT(user_Id)
FROM users, mechanics
WHERE mechanics.id IN (users.mechanic_id)