How to COUNT and SELECT with WHERE in sql? - mysql

I have two tables user and group and also a usergroup as a link table which contains which users exist in a group.
user table
+-----+-------+
| id | name |
+-----+-------+
| U12 | John |
| U13 | Rick |
| U14 | Morty |
| U15 | Alex |
| U16 | Felix |
+-----+-------+
group table
+-----+--------+--------+
| id | name | points |
+-----+--------+--------+
| G12 | Red | 11 |
| G13 | Blue | 22 |
| G14 | Green | 55 |
| G15 | Yellow | 64 |
| G16 | Orange | 23 |
+-----+--------+--------+
usergroup table
+----+---------+----------+
| id | user_id | group_id |
+----+---------+----------+
| 1 | U12 | G12 |
| 2 | U14 | G12 |
| 3 | U15 | G12 |
| 4 | U15 | G15 |
| 5 | U12 | G13 |
+----+---------+----------+
To select the groups that a particular user is in, I can just do
SELECT group.*
FROM usergroup
INNER JOIN group
ON group.id = usergroup.group_id
WHERE usergroup.user_id = ?
But how to also simultaneously select the number of total users in the same group?
Expected Sample Output - for the groups that user U12 is in, along with total user count
+-----+------+--------+-------------+
| id | name | points | users_count |
+-----+------+--------+-------------+
| G12 | Red | 11 | 3 |
| G13 | Blue | 22 | 1 |
+-----+------+--------+-------------+

use scalar subquery
SELECT `group`.* , (select count(id) from usergroup a where a.group_id=usergroup.group_id ) as user_count
FROM usergroup
INNER JOIN `group` ON `group`.id = usergroup.group_id
WHERE usergroup.user_id = 'U12'

SELECT y.id, y.name, y.points, y.users_count
FROM usergroup x INNER JOIN
( SELECT b.id, b.name, b.points, count(a.user_id) as users_count
FROM usergroup a INNER JOIN group b
ON a.group_id = b.id
GROUP BY b.id ) AS y
ON x.group_id = y.id
WHERE x.user_id = 'U12'

SELECT a.group_id,
a.NAME,
a.points,
c.user_count
FROM group a,
usergroup b,
(SELECT x.group_id,
Count(*) AS user_count
FROM usergroup x
GROUP BY x.group_id) c
WHERE a.group_id = b.group_id
AND a.group_id = c.group_id
AND user_id = 'U12';

SELECT group.*, count(usergroup.group_id),usergroup.user_id
FROM usergroup
INNER JOIN group
ON group.id = usergroup.id
GROUP BY usergroup.group_id
Having usergroup.user_id = 'U12'
You need to use group by with having.
Check it on SQLFiddle: sqlfiddle.com/#!9/b63e13/3/0

Related

MYSQL Limit 1 Record per in Joined Table

Attempting to join two tables on user_id. The users table has unique id for each user. The user_codes table can have multiple rows with the same user_id. I only want to return 1 row from the joined user_codes table, where code_count is the largest.
users Table
| id | email |
| -------- | --------------- |
| 1 | user1#gmail.com |
| 2 | user2#gmail.com |
| 3 | user3#gmail.com |
user_code TABLE
| user_id | invite_code | count |
| -------- | ----------- | ------|
| 1 | X49MCL1 | 40 |
| 1 | K59CLT9 | 1000 |
| 2 | X5BC924 | 15 |
| 2 | 38DF80L | 8 |
| 3 | 641020T | 22 |
EXPECTED RESULT
| id | email | invite_code | count |
| --- | --------------- | ----------- | ------|
| 1 | user1#gmail.com | K59CLT9 | 1000 |
| 2 | user2#gmail.com | X5BC924 | 15 |
| 3 | user3#gmail.com | 641020T | 22 |
The query result only includes a single instance of each user found in the user_codes table with the highest count.
Here is the closest query I could get, but it only returns the invite_code and count for the first user.
SELECT a.id, a.email, b.invite_code, b.count
FROM users a
LEFT JOIN user_codes b
ON b.user_id = a.id
AND b.count = (SELECT MAX(count) FROM user_codes GROUP BY b.user_id)
The above query returns the result:
| id | email | invite_code | count |
| --- | --------------- | ----------- | ------ |
| 1 | user1#gmail.com | K59CLT9 | 1000 |
| 2 | user2#gmail.com | `NULL` | `NULL` |
| 3 | user3#gmail.com | `NULL` | `NULL` |
I can't seem to figure out how/why the records after the first one don't include the invite_code and the count.
Thanks for help!
On MySQL 8+, I suggest using the RANK() window function:
WITH cte AS (
SELECT u.id, u.email, uc.invite_code, uc.count,
RANK() OVER (PARTITION BY u.id ORDER BY uc.count DESC) rnk
FROM users u
INNER JOIN user_code uc
ON uc.user_id = u.id
)
SELECT id, email, invite_code, count
FROM cte
WHERE rnk = 1;
The RANK() function will also match multiple records per user tied for the highest count.
You might be able to salvage your current attempt by correlating the user inside the subquery to the outer query:
SELECT a.id, a.email, b.invite_code, b.count
FROM users a
LEFT JOIN user_codes b
ON b.user_id = a.id AND
b.count = (SELECT MAX(uc.count) FROM user_codes uc WHERE uc.user_id = a.id);

MySQL query to count occurrences from multiple tables

I have a problem when I have to select everything from one table (persons) then count how many objects they own by counting their occurrences on other tables (pens, chairs, books)
The current data is as followed:
select * from persons;
+----+-------+
| id | name |
+----+-------+
| 1 | Alex |
| 2 | Brad |
| 3 | Cathy |
+----+-------+
select * from pens;
+----+-----------+
| id | person_id |
+----+-----------+
| 1 | 2 |
| 2 | 2 |
| 3 | 2 |
| 4 | 3 |
+----+-----------+
select * from chairs;
+----+-----------+
| id | person_id |
+----+-----------+
| 1 | 1 |
+----+-----------+
select * from books;
+----+-----------+
| id | person_id |
+----+-----------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----------+
I want the result to be something like this
+----+-------+-----------------------+-------------------------+------------------------+
| id | name | count(pens.person_id) | count(chairs.person_id) | count(books.person_id) |
+----+-------+-----------------------+-------------------------+------------------------+
| 1 | Alex | 0 | 1 | 1 |
| 2 | Brad | 3 | 0 | 1 |
| 3 | Cathy | 1 | 0 | 1 |
+----+-------+-----------------------+-------------------------+------------------------+
I have tried using inner join and left outer join, but join gave me an empty set (since no person matches all of the objects) and left outer join gave me incorrect results:
> select persons.*, count(pens.person_id),count(chairs.person_id),count(books.person_id) from persons join pens on pens.person_id=persons.id join books on books.person_id=persons.id join chairs on chairs.person_id=persons.id group by persons.id;
Empty set (0.002 sec)
> select persons.*, count(pens.person_id),count(chairs.person_id),count(books.person_id) from persons left outer join pens on pens.person_id=persons.id left outer join books on books.person_id=persons.id left outer join chairs on chairs.person_id=persons.id group by persons.id;
# +----+-------+-----------------------+-------------------------+------------------------+
id | name | count(pens.person_id) | count(chairs.person_id) | count(books.person_id) |
# +----+-------+-----------------------+-------------------------+------------------------+
1 | Alex | 0 | 1 | 1 |
2 | Brad | 3 | 0 | 3 |
3 | Cathy | 1 | 0 | 1 |
# +----+-------+-----------------------+-------------------------+------------------------+
Any suggestions will be greatly appreciated, sorry if it's obvious, I'm fairly new at this.
Using a left join approach to subqueries on each table we can try:
SELECT
p.id,
p.name,
COALESCE(ps.cnt, 0) AS cnt_pens,
COALESCE(c.cnt, 0) AS cnt_chairs,
COALESCE(b.cnt, 0) AS cnt_books
FROM persons p
LEFT JOIN
(
SELECT person_id, COUNT(*) AS cnt
FROM pens
GROUP BY person_id
) ps
ON ps.person_id = p.id
LEFT JOIN
(
SELECT person_id, COUNT(*) AS cnt
FROM chairs
GROUP BY person_id
) c
ON c.person_id = p.id
LEFT JOIN
(
SELECT person_id, COUNT(*) AS cnt
FROM books
GROUP BY person_id
) b
ON b.person_id = p.id
ORDER BY
p.id;

Correlated subquery display count column

I have the following query which works fine:
SELECT c.cust_id, c.cust_type_cd, c.city, count(*) as `count`
FROM customer c
INNER JOIN account a
ON a.cust_id = c.cust_id
GROUP BY c.cust_id
HAVING `count` = 2;
Result:
+---------+--------------+---------+-------+
| cust_id | cust_type_cd | city | count |
+---------+--------------+---------+-------+
| 2 | I | Woburn | 2 |
| 3 | I | Quincy | 2 |
| 6 | I | Waltham | 2 |
| 8 | I | Salem | 2 |
| 10 | B | Salem | 2 |
+---------+--------------+---------+-------+
I would like to achieve the same result using a correlated subquery. I have not been able to make a "count" column as show above:
SELECT c.cust_id, c.cust_type_cd, c.city
FROM customer c
WHERE 2 = (
SELECT COUNT(*)
FROM account a
WHERE a.cust_id = c.cust_id
);
Result:
+---------+--------------+---------+
| cust_id | cust_type_cd | city |
+---------+--------------+---------+
| 2 | I | Woburn |
| 3 | I | Quincy |
| 6 | I | Waltham |
| 8 | I | Salem |
| 10 | B | Salem |
+---------+--------------+---------+
How can I achieve the same result as the one using the INNER JOIN and have a "count" column?
Not sure why you wanted this but you also need to specify the subquery in the SELECT part,
SELECT c.cust_id, c.cust_type_cd, c.city, (SELECT COUNT(*)
FROM account a
WHERE a.cust_id = c.cust_id) AS `count`
FROM customer c
WHERE 2 = (
SELECT COUNT(*)
FROM account a
WHERE a.cust_id = c.cust_id
);

Complex MySQL query for specific problemm

I have searched and gone through the available topics similar to mine. But, failed to find that satisfies my requirements. Hence, posting it here.
I have four tables as follows:
"Organization" table:
--------------------------------
| org_id | org_name |
| 1 | A |
| 2 | B |
| 3 | C |
"Members" table:
----------------------------------------------
| mem_id | mem_name | org_id |
| 1 | mem1 | 1 |
| 2 | mem2 | 1 |
| 3 | mem3 | 2 |
| 4 | mem4 | 3 |
"Resource" table:
--------------------------------
| res_id | res_name | res_prop |
| 1 | resource1 | prop-1 |
| 2 | resource2 | prop-2 |
| 3 | resource3 | prop-3 |
| 4 | resource4 | prop-4 |
| 5 | resource1 | prop-5 |
| 6 | resource2 | prop-6 |
A constraint of UNIQUE INDEX (res_name, res_prop) is applied in the above table.
"member-resource" table:
--------------------------------------------
| sl_no | mem_id | res_id |
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 4 | 3 |
| 5 | 3 | 4 |
| 6 | 2 | 3 |
| 7 | 4 | 3 |
| 8 | 1 | 5 |
| 9 | 1 | 6 |
I want to find out the distinct res_name from Resource table that have more than one res_prop for a specific organization. For example, expected output for organization A would be as follows:
| res_name | res_prop_count |
| resource1 | 2 |
| resource2 | 2 |
Any help in this regard will highly be appreciated.
Regards.
When doing things like this you should build your query up logically, so to start get all your resources and props from organisation A
SELECT r.res_name,
r.res_prop
FROM Resource r
INNER JOIN `member-resource` mr
ON mr.res_id = r.res_id
INNER JOIN Members m
ON m.mem_id = mr.mem_id
INNER JOIN Organization o
ON o.org_id = m.org_id
WHERE o.org_name = 'A'
Then you can start thinking about how you want to filter it, so you want to find resource names that have more than one different res_prop, so you need to group by res_name, and apply a HAVING clause to limit it to res_names with more than one distinct res_prop:
SELECT r.res_name,
COUNT(DISTINCT r.res_prop) AS res_prop_count
FROM Resource r
INNER JOIN `member-resource` mr
ON mr.res_id = r.res_id
INNER JOIN Members m
ON m.mem_id = mr.mem_id
INNER JOIN Organization o
ON o.org_id = m.org_id
WHERE o.org_name = 'A'
GROUP BY r.res_name
HAVING COUNT(DISTINCT r.res_prop) > 1;
Example on SQL Fiddle
I'm not entirely sure I understand what you are looking for, but I think this should work:
SELECT Resource.res_name,
COUNT(DISTINCT Resource.res_prop) AS res_prop_count
FROM Resource
INNER JOIN member_resource
USING (res_id)
INNER JOIN Members
USING (mem_id)
INNER JOIN Organization
USING (org_id)
WHERE Organization.org_name = 'A'
GROUP BY res_name
HAVING res_prop_count > 1;
try this:
select
res_name, count(res_prop) as res_prop_count
from
Resource
where
res_id in (select
res_id
from
member_resource
where
mem_id in (select
mem_id
from
Members
where
org_id = (select
org_id
from
Organization
where
org_name = 'A'))) group by res_name
Try this:
If you have Organization ID then use the query below:
SELECT r.res_name, COUNT(DISTINCT r.res_prop) res_prop_count
FROM member-resource mr
INNER JOIN Resource r ON mr.res_id = r.res_id
INNER JOIN Members m ON mr.mem_id = r.mem_id
WHERE m.org_id = 1
GROUP BY r.res_id HAVING res_prop_count > 1;
If you have Organization name then use the query below:
SELECT r.res_name, COUNT(DISTINCT r.res_prop) res_prop_count
FROM member-resource mr
INNER JOIN Resource r ON mr.res_id = r.res_id
INNER JOIN Members m ON mr.mem_id = r.mem_id
INNER JOIN Organization o ON m.org_id = o.org_id
WHERE m.org_name = 'A'
GROUP BY r.res_id HAVING res_prop_count > 1

How must the mysql query be like to achieve the shown result?

Say, I have two tables like these:
Table group Table user
+----+-----------+ +----+----------+------+----------+
| id | groupname | | id | username | rank | group_id |
+----+-----------+ +----+----------+------+----------+
| 1 | Friends | | 1 | Frank | 1 | 1 |
| 2 | Family | | 2 | Mike | 3 | 1 |
+----+-----------+ | 3 | Steve | 2 | 1 |
| 4 | Tom | 1 | 2 |
+----+----------+------+----------+
And I want to select all groups and get the user with the highest rank (the highest number) for each group. So basically I want to get this result:
+-----------------+----------+---------+---------------+
| group.groupname | group.id | user.id | user.username |
+-----------------+----------+---------+---------------+
| Friends | 1 | 2 | Mike |
| Family | 2 | 4 | Tom |
+-----------------+----------+---------+---------------+
How has the select to be like?
It maybe very simple, but I'm not getting it right now....
Edit 2:
My previous answer was wrong, the max() call destroyed the result. Here's a correct solution:
SELECT g.groupname, g.id AS group_id, u.id AS user_id, u.username
FROM `user` u
LEFT JOIN `group` g ON (u.group_id=g.id)
WHERE u.rank=(
SELECT MAX(rank)
FROM `user` u2
WHERE u.group_id=u2.group_id
)
The check in the WHERE clause should be more understandable, too.
mysql> SELECT g.groupname, g.id AS group_id, u.id AS user_id, u.username
-> FROM `user` u
-> LEFT JOIN `group` g ON (u.group_id=g.id)
-> WHERE u.rank=(
-> SELECT MAX(rank)
-> FROM `user` u2
-> WHERE u.group_id=u2.group_id
-> );
+-----------+----------+---------+----------+
| groupname | group_id | user_id | username |
+-----------+----------+---------+----------+
| Friends | 1 | 2 | Mike |
| Family | 2 | 4 | Tom |
+-----------+----------+---------+----------+
2 rows in set (0.00 sec)
select g.groupname, u.group_id, u.id as user_id, u.username
from group g
inner join (
select group_id, max(rank) as MaxRank
from user
group by group_id
) um on g.id = um.group_id
inner join user u on um.group_id = u.group_id and um.MaxRank = u.rank