I have table All Badges and table User Badge.
The value in User Badge can all be found on the All Badges and each user_id can have multiple User Badge so I use GROUP_CONCAT to group the value based on the user_id. The goal of the User Badge is to allow users to collect all available badges, thus they will need information about the missing badge on their profile.
Here is the All Badges table:
|---------------|-----------|
| Badge_ID | Label |
|---------------|-----------|
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
|---------------|-----------|
The User Badge table:
|------------------|---------------|---------------|---------------|
| User_Badge_ID | Badge_Label | user_ID | fk_badge_id |
|------------------|---------------|---------------|---------------|
| 1 | a | 1 | 1 |
| 2 | a | 2 | 1 |
| 3 | b | 1 | 2 |
| 4 | a | 3 | 1 |
| 5 | b | 2 | 2 |
| 6 | c | 2 | 3 |
| 7 | d | 2 | 4 |
|------------------|---------------|---------------|---------------|
I have this query to group the badge on user_id, but I don't know the query to show the missing value.
SELECT
user_badge.user_id AS User_ID
GROUP_CONCAT(user_badge.Badge_Label) AS User_Badge
FROM
User_Badge
The desired result. The missing badge will be added into the new column called Missing Badge.
|---------------------|------------------|------------------|
| User_ID | User_Badge | Missing Badge |
|---------------------|------------------|------------------|
| 1 | a, b | c, d, e |
|---------------------|------------------|------------------|
| 2 | a, b, c, d | e |
|---------------------|------------------|------------------|
| 3 | a | b, c, d, e |
|---------------------|------------------|------------------|
Any ideas to show the missing value from each user_id? Many thanks for your willingness to help.
select s.user_id,
group_concat(case when fk_badge_id is not null then s.label end) 'user_badge',
group_concat(case when fk_badge_id is null then s.label end) 'missing'
from
(
SELECT distinct USER_ID,badge_id,ab.label
FROM USER_BADGE ub
CROSS JOIN ALL_BADGES ab
) s
left join user_badge ub on ub.user_id = s.user_id and ub.fk_badge_id = s.badge_id
group by s.user_id;
Where the sub query s gets all the badges a user could have using a cross join and the main query checks those he/she has or not , the left join returning nulls if has not.
+---------+------------+---------+
| user_id | user_badge | missing |
+---------+------------+---------+
| 1 | a,b | c,d,e |
| 2 | a,b,c,d | e |
| 3 | a | c,d,e,b |
+---------+------------+---------+
3 rows in set (0.00 sec)
Related
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;
table 1: forum_threads
+-----+------+-------+
| id | title| status|
+-----+------+-------+
| 1 | a | 1 |
| 2 | b | 1 |
| 3 | c | 1 |
| 4 | d | 1 |
| 5 | e | 1 |
| 6 | f | 1 |
+-----+------+-------+
table 2: forum_comments
+-----+----------+--------------------+
| id | thread_id| comment |
+-----+----------+--------------------+
| 1 | 4 | hai |
| 2 | 4 | hello |
| 3 | 2 | welcome |
| 4 | 2 | whats your name |
| 5 | 6 | how are you |
| 6 | 5 | how old are you |
| 7 | 5 | good |
+-----+----------+--------------------+
wanted output
+-----------+----------+-----------------+
| thread_id | title | comment_count |
+-----------+----------+-----------------+
| 5 | e | 2 |
| 6 | f | 1 |
| 2 | b | 2 |
| 4 | d | 2 |
+-----------+----------+-----------------+
my Query
SELECT forum_threads.*,forum_comments.*,count(forum_comments.id) as comment_count
FROM forum_comments
LEFT JOIN forum_threads ON forum_comments.thread_id = forum_threads.id
GROUP BY forum_threads.id
ORDER BY forum_comments.id desc
Here I am trying to get the titles by the latest comment.
when I give ORDER BY forum_comments.id this returns the wrong order.
I need to order by the latest comments in the forum_comments table.
this query returns the wrong order please help me to find out the correct order.
how could I solve this easily?
This query should give you the expected result:
select t2.thread_id, t1.title, t2.comment_count from forum_threads as t1,
(SELECT id, thread_id, count(comment) as comment_count from forum_comments group by thread_id) as t2
where t1.id = t2.thread_id order by t2.id desc;
Instead of using forum_threads.* and forum_comments.* can you give specific column names and try.
If that doesn't work you should try explicitly assigning primary and foreign keys.
I have two MySQL tables (table_a and table_b) and a join table (table_c).
Table Structures:
table_a:
__________________
| table_a: |
|----------------|
| id |
| result_column |
------------------
table_b:
__________________
| table_b: |
|----------------|
| id |
| name |
------------------
table_c:
__________________
| table_c: |
|----------------|
| id |
| table_a_id |
| table_b_id |
------------------
My Goal:
I want to find a query that will:
Iterate over every table_a record and get the table_a.id value
Find any records in table_c which have a matching table_c.table_a_id value
For each matching record in table_c get the table_c.table_b_id value
Find the record in table_b which has a matching table_b.id value
For that matching record in table_b get the table_b.name value
In table_a, concatenate each matched name value into the corresponding table_a.result_column
Example:
Before the Query:
_______________________ _________________________________ ________________
| table_a: | | table_c: | | table_b: |
|---------------------| |-------------------------------| |--------------|
| id | result_column | | id | table_a_id | table_b_id | | id | name |
|-----|---------------| |-----|------------|------------| |-----|--------|
| 1 | | | 1 | 1 | 3 | | 1 | Kevin |
| 2 | | | 2 | 1 | 4 | | 2 | Jesse |
| 3 | | | 3 | 2 | 2 | | 3 | Karen |
----------------------- | 4 | 3 | 1 | | 4 | Tim |
| 5 | 3 | 5 | | 5 | Lauren |
--------------------------------- ----------------
After the Query:
_______________________ _________________________________ ________________
| table_a: | | table_c: | | table_b: |
|---------------------| |-------------------------------| |--------------|
| id | result_column | | id | table_a_id | table_b_id | | id | name |
|-----|---------------| |-----|------------|------------| |-----|--------|
| 1 | Karen, Tim | | 1 | 1 | 3 | | 1 | Kevin |
| 2 | Jesse | | 2 | 1 | 4 | | 2 | Jesse |
| 3 | Kevin, Lauren | | 3 | 2 | 2 | | 3 | Karen |
----------------------- | 4 | 3 | 1 | | 4 | Tim |
| 5 | 3 | 5 | | 5 | Lauren |
--------------------------------- ----------------
For absolute clarity, I understand that this is incredibly bad practice within a relational data-table. This is as far from normalization as one can get. I would never design a database like this. I was tasked with creating a custom column with a list of values purely for a business case.
The query you seem to want is:
select c.table_a_id, group_concat(b.name separator ', ')
from c join
b
on c.table_b_id = b.id
group by c.table_a_id;
If you actually want to update a, you can put this into an update statement:
update a join
(select c.table_a_id, group_concat(b.name separator ', ') as names
from c join
b
on c.table_b_id = b.id
group by c.table_a_id
) cb
on cb.table_a_id = a.id
set result_column = cb.names
Previous answer is close; but you also required that you only want the records matched in table C that are in A.
The first query does not meet this requirement; but the update statement does, as it will only update records in table A, if the id matches the table_a_id value pulled from table C.
Given what you said you wished for the end result, the update statement above would work.
If you wish to be explicit in your logic, just add a join from table A to table C.
select a.id, group_concat(b.name separator ', ')
from a
join c ON (a.id = c.table_a_id)
join b ON (c.table_b_id = b.id)
group by a.id;
I'm trying to figure out a MYSQL string and my noob-ness is getting in my way. I'm trying to count the total number of teams per phase.
Tables to consider:
phases
+----+------------+
| id | phase_name |
+----+------------+
| 1 | start |
| 2 | middle |
| 3 | end |
| 4 | finish |
+----+------------+
teams
+----+-----------+----------+
| id | team_name | phase_id |
+----+-----------+----------+
| 1 | team1 | 2 |
| 2 | team2 | 3 |
| 3 | team3 | 3 |
| 4 | team4 | 4 |
| 4 | team5 | 3 |
+----+-----------+----------+
Desired result
+----------+------------+-----------+
| phase_id | phase_name | tot_teams |
+----------+------------+-----------+
| 1 | start | NULL |
| 2 | middle | 1 |
| 3 | end | 3 |
| 4 | finish | 1 |
+----------+------------+-----------+
I've tried:
SELECT
T.phase_id, P.phase_name, COUNT(*) AS tot_teams
FROM
teams T
LEFT JOIN
phases P ON P.id = T.phase_id
GROUP BY
phase_id;
but that only shows the affected phase_id's...and I'm hoping to get ALL phase_id's in a table. I also tried:
SELECT
P.phase_name, T.phase_id, COUNT(*)
FROM
teams T
RIGHT JOIN
phases P on P.`id` = T.`phase_id`
GROUP BY
P.id
but that shows invalid data. (For example, phase_id has a qty of 1 but doesn't show up in the teams table.
Can you point me in the right direction?
Thanks!
The RIGHT JOIN is correct, but you need to use COUNT(T.phase_id) instead of COUNT(*). Otherwise, you're counting the row containing NULL that's generated for the phase with no teams.
Most people prefer to use LEFT JOIN, putting the master table first.
SELECT P.phase_name, P.phase_name, COUNT(T.phase_id)
FROM phase AS P
LEFT JOIN teams AS T ON P.id = T.phase_id
GROUP BY P.id
I'm trying to put together a summary table that has counts of types of mail sent by group.
Hopefully the below is enough to explain what I mean.
Table 1 (senders)
| id | name | group_id |
+----+------+----------+
| 1 | mike | 1 |
| 2 | john | 1 |
| 3 | lucy | 2 |
| 4 | lobo | 3 |
Table 2 (mail)
| id | type | sender_id |
+----+----------+-----------+
| 1 | letter | 1 |
| 2 | postcard | 2 |
| 3 | postcard | 1 |
| 4 | letter | 2 |
| 5 | postcard | 2 |
| 6 | postcard | 4 |
Table 3 (groups)
| id | name | active |
+----+-------+--------+
| 1 | alpha | 1 |
| 2 | black | 1 |
| 3 | cero | 0 |
Ideal result
| group | letter | postcard | parcel |
+-------+--------+----------+--------+
| alpha | 2 | 3 | 0 |
| black | 0 | 0 | 0 |
So I need to get counts per mail type for active groups.
I've been working through examples (only learning MySQL) but when I think of this situation I'm just totally blank.
Have looked at the answers to Joining three tables to get summary data in MySQL but I don't quite understand how to translate the answers to my problem.
Any help is appreciated.
SELECT t.name,
MAX(CASE t.TYPE WHEN 'letter' THEN #CS:=#CS+1 ELSE 0 END ) letter,
MAX(CASE t.TYPE WHEN 'postcard' THEN #CS1:=#CS1+1 ELSE 0 END ) postcard ,
MAX(CASE t.TYPE WHEN 'parcel ' THEN #CS2:=#CS2+1 ELSE 0 END ) parcel
FROM
(SELECT
groups. name,
mail.type
FROM
groups
LEFT JOIN senders ON groups.id = senders.id
LEFT JOIN mail ON senders.id = mail.sender_id ) AS t
,(SELECT #CS:=0) CS ,(SELECT #CS1:=0) CS1 ,(SELECT #CS2:=0) CS2
You put this query
Select count(*) from senders s inner join mail m on s.id = s.sender_id inner join
groups g on s.groups_id = g.id group by m.type