replace IDs from GROUP_CONCAT column with names - mysql

I have three tables which I successfully joined with the following sql query
SELECT `bonuses`.`id`, `bonuses`.`bonus_name`, `bonuses`.`size`, creatorName.`name`, GROUP_CONCAT(DISTINCT bonus_user.user_id ORDER BY bonus_user.user_id SEPARATOR ', ') as bonusUsers from `bonuses`
inner join `users` as creatorName on `bonuses`.`created_from` = creatorName.`id`
inner join `bonus_user` on `bonuses`.`id` = `bonus_user`.`bonus_id`
group by `bonuses`.`id`
The result I get is as following. As a further step I want to replace the ids in the column "bonusUsers" by the names from the users table. How do I manage this?
+----+--------------+------+--------------+--------------+
| id | bonus_name | size | name | bonusUsers |
+----+--------------+------+--------------+--------------+
| 3 | Bonus Test 3 | 5 | Test1 | 1, 2, 3 |
| 4 | Bonus Test 4 | 3 | Test1 | 1, 2, 3 |
+----+--------------+------+--------------+--------------+
users
+----+-------+
| id | name |
+----+-------+
| 1 | Test1 |
| 2 | Test2 |
| 3 | Test3 |
+----+-------+
bonuses
+----+--------------+------+--------------+
| id | bonus_name | size | created_from |
+----+--------------+------+--------------+
| 1 | Bonus Test 1 | 1 | 1 |
| 2 | Bonus Test 2 | 1 | 1 |
| 3 | Bonus Test 3 | 5 | 1 |
| 4 | Bonus Test 4 | 3 | 1 |
+----+--------------+------+--------------+
bonus_user
+----+----------+------------+
| id | bonus_id | bonus_user |
+----+----------+------------+
| 1 | 3 | 1 |
| 2 | 3 | 2 |
| 3 | 3 | 3 |
| 4 | 4 | 1 |
| 5 | 4 | 2 |
| 6 | 4 | 3 |
+----+----------+------------+

Do another join with users table
SELECT `b`.`id`, `b`.`bonus_name`, `b`.`size`, u.`name`,
GROUP_CONCAT(DISTINCT bu1.`name` ORDER BY bu.user_id SEPARATOR ', ') as bonusUsers
from `bonuses` b
inner join `users` as u on `b`.`created_from` = u.`id`
inner join `bonus_user` bu on `b`.`id` = `bu`.`bonus_id`
inner join `users` as bu1 on `bu`.`user_id` = bu1.`id`
group by `b`.`id`, `b`.`bonus_name`, `b`.`size`, u.`name`
Demo

You need to aliase the users table and join it to the bonus_user tables to get the name of the user from the aliased table
SELECT `bonuses`.`id`, `bonuses`.`bonus_name`, `bonuses`.`size`, creatorName.`name`,
GROUP_CONCAT(DISTINCT user_names.name ORDER BY bonus_user.user_id SEPARATOR ', ') as bonusUsers from `bonuses`
inner join `users` as creatorName on `bonuses`.`created_from` = creatorName.`id`
inner join `bonus_user` on `bonuses`.`id` = `bonus_user`.`bonus_id`
inner join `users AS user_names` on `bonus_user`.`bonus_user` = `user_names`.`id`
group by `bonuses`.`id`
This may help as well https://www.w3schools.com/sql/sql_alias.asp

Related

Selecting COUNT and MAX columns with 2 tables and a bridge table

so what I am trying to do is having 3 tables (pictures, collections, and bridge) with the following columns:
Collections Table:
| id | name |
------------------
| 1 | coll1 |
| 2 | coll2 |
------------------
Pictures Table: (timestamps are unix timestamps)
| id | name | timestamp |
-------------------------
| 5 | Pic5 | 1 |
| 6 | Pic6 | 19 |
| 7 | Pic7 | 3 |
| 8 | Pic8 | 892 |
| 9 | Pic9 | 4 |
-------------------------
Bridge Table:
| id | collection | picture |
-----------------------------
| 1 | 1 | 5 |
| 2 | 1 | 6 |
| 3 | 1 | 7 |
| 4 | 1 | 8 |
| 5 | 2 | 5 |
| 6 | 2 | 9 |
| 7 | 2 | 7 |
-----------------------------
And the result should look like this:
| collection_name | picture_count | newest_picture |
----------------------------------------------------
| coll1 | 4 | 8 |
| coll2 | 3 | 9 |
----------------------------------------------------
newest_picture should always be the picture with the heighest timestamp in that collection and I also want to sort the result by it. picture_count is obviously the count of picture in that collection.
Can this be done in a single statement with table joins and if yes:
how can I do this the best way?
A simple method uses correlated subqueries:
select c.*,
(select count(*)
from bridge b
where b.collection = c.id
) as pic_count,
(select p.id
from bridge b join
pictures p
on b.picture = b.id
where b.collection = c.id
order by p.timestamp desc
limit 1
) as most_recent_picture
from collections c;
A more common approach would use window functions:
select c.id, c.name, count(bp.collection), bp.most_recent_picture
from collections c left join
(select b.*,
first_value(p.id) over (partition by b.collection order by p.timestamp desc) as most_recent_picture
from bridge b join
pictures p
on b.picture = p.id
) bp
on bp.collection = c.id
group by c.id, c.name, bp.most_recent_picture;

GROUP by version and display by row

+----------+--------+
| name | version|
+----------+--------+
| book | 2 |
| book | 1 |
| book | 1 |
| pen | 1 |
| pen | 2 |
| pen | 2 |
| pen | 2 |
| paper | 1 |
+----------+--------+
I have the table above and i want to make a query to group by name and count by version(row)
Result:
+----------+--------+--------+
| name | version| count |
+----------+--------+--------+
| book | 1 | 2 |
| book | 2 | 1 |
| pen | 1 | 1 |
| pen | 2 | 3 |
| paper | 1 | 1 |
| paper | 2 | 0 |
+----------+--------+--------+
The query would be
SELECT name, version, count(*) as count
FROM your_table_name
GROUP BY name, version
If you want all name/version combinations, then use a cross join to generate all rows and then left join to bring in the existing data:
select n.name, v.version, count(t.name)
from (select distinct name from t) n cross join
(select distinct version from t) v left join
t
on t.name = n.name and t.version = v.version
group by n.name, v.version
order by n.name, v.version;

How to join from 3 table with condition

I have 3 table inside my database, my first table is person table its store all person name, my second table is hobby table its store all hobby of all person,the third table is referensi table store all references person and hobby.
Tabel person : Tabel hobby : Tabel referensi :
----------------- ------------------ -------------------------------------
| id | name | | id | hobby | | id | ref_person | ref_hobby |
----------------- ------------------ -------------------------------------
| 1 | Rose | | 1 | Makan | | 1 | 1 | 1 |
| 2 | Lisa | | 2 | Renang | | 2 | 1 | 3 |
| 3 | Jisoo | | 3 | Nyanyi | | 3 | 1 | 4 |
| 4 | Jennie| | 4 | Youtube| | 4 | 3 | 5 |
----------------- | 5 | Masak | -------------------------------------
------------------
I want to count all hobby by that person
Example I want select Rose : Or I want select Jisoo :
--------------------------- ---------------------------
| id | hobby | count | | id | hobby | count |
--------------------------- ---------------------------
| 1 | Makan | 1 | | 1 | Makan | 0 |
| 2 | Renang | 0 | | 2 | Renang | 0 |
| 3 | Nyanyi | 1 | | 3 | Nyanyi | 0 |
| 4 | Youtube| 1 | | 4 | Youtube| 0 |
| 5 | Masak | 0 | | 5 | Masak | 1 |
--------------------------- ---------------------------
And so forth, how do I solve this problem?
This is my query that I write but doesn't seem to work, because only data with count greater than 0 is shown.
SELECT
hobby.id,
hobby.name,
count( referensi.id ) AS count
FROM
referensi
LEFT OUTER JOIN hobby ON hobby.id = referensi.ref_hobby
JOIN person ON referensi.ref_person = person.id
WHERE person.id = 1
GROUP BY
hobby.id
Thanks in advance.
To solve this you need to JOIN referensi to person, selecting only entries in referensi corresponding to the person of interest, and then RIGHT JOIN to hobby. If there is no matching entry, the output is 0, otherwise 1. For example, for person 1:
SELECT h.id,
h.hobby,
CASE WHEN r.id IS NULL THEN 0 ELSE 1 END AS count
FROM referensi r
JOIN person p ON p.id = r.ref_person AND p.id = 1
RIGHT JOIN hobby h ON h.id = r.ref_hobby
ORDER BY h.id
This can also be implemented with a correlated subquery:
SELECT h.id,
h.hobby,
EXISTS (SELECT * FROM referensi r WHERE r.ref_hobby = h.id AND r.ref_person = 1) AS count
FROM hobby h
If a person/hobby tuple can appear in the referensi table more than once, you do need to do a COUNT:
SELECT h.id,
h.hobby,
COUNT(r.id) AS count
FROM referensi r
JOIN person p ON p.id = r.ref_person AND p.id = 1
RIGHT JOIN hobby h ON h.id = r.ref_hobby
GROUP BY h.id
Output (for all three queries on your sample data):
id hobby count
1 Makan 1
2 Renang 0
3 Nyanyi 1
4 Youtube 1
5 Masak 0
Demo on SQLFiddle
You can try to use condition aggravate function with OUTER JOIN
setting condition in CASE WHEN
Query 1:
SELECT
hobby.id,
hobby.name,
COUNT(CASE WHEN person.id = 3 THEN 1 END) AS count
FROM
hobby
LEFT JOIN referensi ON hobby.id = referensi.ref_hobby
LEFT JOIN person ON referensi.ref_person = person.id
GROUP BY
hobby.id,
hobby.name
Results:
| id | name | count |
|----|---------|-------|
| 1 | Makan | 0 |
| 2 | Renang | 0 |
| 3 | Nyanyi | 0 |
| 4 | Youtube | 0 |
| 5 | Masak | 1 |
You want to start your joining from hobby table, and use LEFT JOINs to optionnaly bring up the matching records in other tables.
SELECT
h.id,
h.hobby,
count( p.id ) AS count
FROM
hobby h
LEFT JOIN referensi r ON h.id = r.ref_hobby
LEFT JOIN person p ON r.ref_person = p.id AND p.id = 1
WHERE p.name is NULL OR p.name = 'Rose'
GROUP BY h.id, h.hobby
It is also a good practice to use table aliases, I added them to your query.
Demo on DB Fiddle for user Rose :
| id | hobby | count |
| --- | ------- | ----- |
| 1 | Makan | 1 |
| 2 | Renang | 0 |
| 3 | Nyanyi | 1 |
| 4 | Youtube | 1 |
| 5 | Masak | 0 |

Getting specific values from many-to-many relationships

My database looks like this, I have client accounts which are assigned to specific profiles, and I have profiles which are assigned to specific categories, like in this schema:
| categories | | profiles | | categories_map |
--------------- ------------- ----------------------------
| ID | name | | ID | name | | ID | profile_id | cat_id |
--------------- ------------- ----------------------------
| 1 | cat1 | | 1 | p1 | | 1 | 1 | 1 |
| 2 | cat2 | | 2 | p2 | | 2 | 2 | 1 |
| 3 | cat3 | | 3 | p3 | | 3 | 3 | 1 |
| 4 | p4 | | 4 | 1 | 2 |
| 5 | 3 | 2 |
| 6 | 4 | 3 |
| profiles_map |
-----------------------------
| ID | profile_id | acc_id |
-----------------------------
| 1 | 1 | 1 |
| 2 | 3 | 1 |
| 3 | 4 | 1 |
I need to get categories assigned to accounts - which means when I want to get categories for acc_id = 1, I should get categories with ID 2 and 3 ( category with ID 2 doesn't fit because it contains profile with ID 2 which isn't assigned to this account). I tried this query but it doesn't work
select cats.id from profiles_map map
right join categories_map catm on catm.profile_id = map.profile_id
right join categories cats on cats.id = catm.cat_id
where catm.profile_id in (select profile_id from profiles_map where acc_id = 1)
and map.acc_id = 1 group by cats.id;
Could anybody help me with this question?
Can you try this one?
SELECT DISTINCT C.ID
FROM profiles_map PM
INNER JOIN categories_map CM ON CM.profile_id = PM.profile_id
INNER JOIN categories C ON C.ID = CM.cat_id
WHERE PM.acc_id= 1
If you want to get only category id, Please try following query:
SELECT DISTINCT cm.cat_id FROM categories_map cm
WHERE cm.profile_id in
(SELECT profile_id FROM profiles_map WHERE acc_id = 1)
Or if want to get category name and id then , use following query:
SELECT cat.id,cat.name FROM categories cm
WHERE cat.id in (SELECT DISTINCT cm.cat_id FROM categories_map cm
WHERE cm.profile_id in
(SELECT pm.profile_id FROM profiles_map pm WHERE pm.acc_id = 1))

group Items by column and order by other column

I have table as below , I want to take latest rating for the client
basically user whenever updates rating, count will be incremented and a entry will be made in table. Table goes as below
-----------------------------------------------------
|_id| name | client_id | user_id | rating | count |
-----------------------------------------------------
|1 | Four | 1 | 1 | 4 | 1 |
|2 | three | 1 | 1 | 3 | 2 |
|3 | two | 1 | 1 | 2 | 3 |
|4 | five | 1 | 1 | 5 | 4 |
|5 | two | 1 | 2 | 2 | 1 |
|6 | three | 1 | 2 | 3 | 2 |
|7 | two | 2 | 1 | 2 | 1 |
|8 | three | 2 | 1 | 3 | 2 |
-----------------------------------------------------
For rating of client_id 1 I want out put like
-----------------------------------------------------
|_id| name | client_id | user_id | rating | count |
-----------------------------------------------------
|4 | five | 1 | 1 | 5 | 4 |
|6 | three | 1 | 2 | 3 | 2 |
-----------------------------------------------------
so far I tried SELECT * FROM test
where client_id = 1 group by client_id order by count desc;
but not getting expected result, any help??
You can use left join on the same table as
select t1.* from test t1
left join test t2 on t1.user_id = t2.user_id
and t1.client_id = t2.client_id
and t1._id < t2._id
where
t2._id is null
and t1.client_id = 1
order by t1.`count` desc;
Using un-correlated subquery you may do as
select t1.* from test t1
join (
select max(_id) as _id,
client_id,
user_id
from test
where client_id = 1
group by client_id,user_id
)t2
on t1._id = t2._id
and t1.client_id = t2.client_id
order by t1.`count` desc;
UPDATE : From the comment how to join another table into above , for this here is an example
mysql> select * from users ;
+------+------+
| _id | name |
+------+------+
| 1 | AAA |
| 2 | BBB |
+------+------+
2 rows in set (0.00 sec)
mysql> select * from test ;
+------+-------+-----------+---------+--------+-------+
| _id | name | client_id | user_id | rating | count |
+------+-------+-----------+---------+--------+-------+
| 1 | four | 1 | 1 | 4 | 1 |
| 2 | three | 1 | 1 | 3 | 2 |
| 3 | two | 1 | 1 | 2 | 3 |
| 4 | five | 1 | 1 | 5 | 4 |
| 5 | two | 1 | 2 | 2 | 1 |
| 6 | three | 1 | 2 | 3 | 2 |
| 7 | two | 2 | 1 | 2 | 1 |
| 8 | three | 2 | 1 | 3 | 2 |
+------+-------+-----------+---------+--------+-------+
select t1.*,u.name from test t1
join users u on u._id = t1.user_id
left join test t2 on t1.user_id = t2.user_id
and t1.client_id = t2.client_id
and t1._id < t2._id
where
t2._id is null
and t1.client_id = 1
order by t1.`count` desc;
Will give you
+------+-------+-----------+---------+--------+-------+------+
| _id | name | client_id | user_id | rating | count | name |
+------+-------+-----------+---------+--------+-------+------+
| 4 | five | 1 | 1 | 5 | 4 | AAA |
| 6 | three | 1 | 2 | 3 | 2 | BBB |
+------+-------+-----------+---------+--------+-------+------+
Note that the join to users table is inner join and this will require all the user to be preset in users table which are in test table
If some users are missing in the users table then use left join this will have null values for the data selected from users table.
You may try something like
select _id, name, client_id, user_id, rating, max(count)
from clients
group by client_id
Try it
SELECT * FROM test
where client_id = 1
group by user_id
order by count desc