Using nested SELECT result for IN statement of another nested SELECT - mysql

Be gentle. I'm a high school principal coding on the side for our school site.
I have looked at answers, here, here, and here. I might just not know enough to ask the right question.
We have events that have multiple sessions and there are workshops that can be associated with multiple sessions in multiple events.
I'm trying to get a csv result, later to be put into an array, for the associated sessions and events for my Workshops.
The query below works without the second nested Select statement.
In the Alt_Events statement, I need to pull the Event_IDs that are associated with the Session_IDs that are pulled from the first nested Select.
Events
ID | Name | Description
1 | Flex Learning | A day of flexible learning.
2 | Moonshot Expo | A day to join partners to solve problems.
Event_Sessions
ID | Event_ID | Name | Description
1 | 1 | Morning Session | The first session of the day.
2 | 1 | Afternoon Session | The afternoon session.
3 | 1 | Tutoring Session | A chance to get help from teachers.
4 | 2 | Partner Field Trip | The first session of the day.
5 | 2 | Brainstorming Session | The afternoon session.
6 | 2 | Tutoring Session | A chance to get help from teachers.
Event_Workshops
ID | Name | Description
1 | Math Tutorial | Get help from your math teachers.
Event_Workshop_Links
ID | Workshop_ID | Session_ID
1 | 1 | 3
2 | 1 | 6
Output Table:
ID | Name | Description | ... | Alt_Sessions | Alt_Events
1 | Math Tutorial | Get help... | ... | 3,6 | 1,2
Here is my query.
SELECT
ws.ID, ws.Name, ws.Description, ws.Location, ws.Owner_ID, ws.Max_Attendees,
ws.Eng_Major_Allowed, ws.Eng_Minor_Allowed,
ws.HC_Major_Allowed, ws.HC_Minor_Allowed,
ws.IT_Major_Allowed, ws.IT_Minor_Allowed,
u.LastName as Owner_LastName, u.FirstName AS Owner_FirstName, u.Email AS Owner_Email,
(SELECT group_concat(SESSION_ID) FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID) AS Alt_Sessions,
(SELECT group_concat(Event_ID) FROM Event_Sessions WHERE Session_ID IN Alt_Sessions) AS Alt_Events
FROM Event_Workshops as ws
LEFT JOIN users AS u
ON ws.Owner_ID = u.ID
WHERE ws.ID = ?
ORDER BY ws.Name
I need to be able to pull the all event_ids that are in the Alt_Sessions result.
I'm guessing I can't use the result of the first nested query in the second nested query. If that's the problem, how can I pull that list of event ids?
Any and all help is greatly appreciated.
(Updated to show expected output. Also one error in transcribing the query. Session_ID instead of Event_ID in second nested statement.

Use the subquery instead of Alt_Sessions in the IN predicate like below.
(SELECT group_concat(SESSION_ID) FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID) AS Alt_Sessions,
(SELECT group_concat(Event_ID) FROM Event_Sessions WHERE Session_ID IN (SELECT SESSION_ID FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID)) AS Alt_Events
Also, there is a way to make combinations of Alt_Sessions and Alt_Events first and then join to Event_Workshops.
SELECT * FROM Event_Workshops ws
JOIN
(
SELECT
wsl.Workshop_ID,
GROUP_CONCAT(wsl.Session_ID) Alt_Sessions,
GROUP_CONCAT(wsl.ID) Alt_Events
FROM Event_Workshop_Links wsl
GROUP BY wsl.Workshop_ID
) w
ON ws.ID = w.Workshop_ID

Related

Join on a string with individual values inside commas [duplicate]

This question already has answers here:
Is storing a delimited list in a database column really that bad?
(10 answers)
Closed 2 years ago.
I have a table (called toy table) which shows for each toy type the sales ID processed, with commas between different sales IDs. Please note that my data schema is not this way. But I aggregate data on the toy level, to generate a report.
|------------------------|------------------------|
| Toy | IDs |
|------------------------|------------------------|
| Buzz Lightyear | 22,33,44 |
| Woody | 24,41 |
|------------------------|------------------------|
I have another table (called status table) which has status for each order ID.
|------------------------|------------------------|
| ID | Status |
|------------------------|------------------------|
| 22 | running |
| 33 | paused |
| 44 | running |
| 24 | cancelled |
| 41 | finished |
|------------------------|------------------------|
I want to make a table (through a join) in which I can have a column for status separated by commas, in the right order of sales IDs, and also a column for running IDs only. It would look like this:
|------------------------|------------------------|------------------------|-----------------------|
| Toy | IDs | Status | Running IDs. |
|------------------------|------------------------|------------------------|-----------------------|
| Buzz Lightyear | 22,33,44 |running, paused, running| 22,44 |
| Woody | 24,41 | cancelled, finished | |
|------------------------|------------------------|------------------------|-----------------------|
I dont know how to make the join. I've tried finding the position of ID through find_in_set() but I just can't seem to progress after that. I have to report on the toy level so there cannot be more than one toy in a row in the final dataset.
You need to do your JOINs before you do the aggregation. Without seeing your table structures it's hard to be 100% certain, but the query you want should look something like this:
SELECT t.Toy,
GROUP_CONCAT(o.ID ORDER BY o.ID) AS IDs,
GROUP_CONCAT(s.Status ORDER BY s.ID) AS Status,
GROUP_CONCAT(CASE WHEN s.Status = 'running' THEN s.ID END) AS `Running IDs`
FROM toys t
JOIN orders o ON o.Toy_ID = t.ID
JOIN status s ON s.ID = o.ID
GROUP BY t.Toy
Demo on SQLFiddle

MYSQL : Group by all weeks of a year with 0 included

I have a question about some mysql code.
I have a table referencing some employees with the date of arrival et the project id. I wanna calculate all the entries in the enterprise and group it by week.
A this moment, I can have this result
Project ID | Week | Count
1 | 2019-S01 | 2
1 | 2019-S03 | 1
2 | 2019-S01 | 1
2 | 2019-S04 | 5
2 | 2019-S05 | 3
2 | 2019-S06 | 2
This is good, but I would like to have all the weeks returned, even if a week has 0 as result :
Project ID | Week | Count
1 | 2019-S01 | 2
1 | 2019-S02 | 0
1 | 2019-S03 | 1
...
2 | 2019-S01 | 1
2 | 2019-S02 | 0
2 | 2019-S03 | 0
2 | 2019-S04 | 5
2 | 2019-S05 | 3
2 | 2019-S06 | 2
...
Here is my actual code :
SELECT
AP.SECTION_ANALYTIQUE AS SECTION,
FS_GET_FORMAT_SEMAINE(AP.DATE_ARRIVEE_PROJET) AS SEMAINE,
Count(*) AS COMPTE
FROM
RT00_AFFECTATIONS_PREV AP
WHERE
(AP.DATE_ARRIVEE_PROJET <= CURDATE() AND Year(AP.DATE_ARRIVEE_PROJET) >= Year(CURDATE()))
GROUP BY
SECTION, SEMAINE
ORDER BY
SECTION
Does anybody have a solution ?
I searched things on internet but didn't find anything accurate :(
Thank you in advance ! :)
The classic way to meet this requirement is to create a referential table to store all possible weeks.
create table all_weeks(week varchar(8) primary key);
insert into all_weeks values
('2019-S01'), ('2019-S02'), ('2019-S03'), ('2019-S04'), ('2019-S05'), ('2019-S06');
Once this is done, you can generate a cartesian product of all possible sections and weeks with a CROSS JOIN, and LEFT JOIN that with the original table.
Given your code snippet, this should look like:
SELECT
s.section_analytique AS section,
w.week AS semaine,
COUNT(ap.section_analytique) AS compte
FROM
(SELECT DISTINCT section_analytique from rt00_affectations_prev) s
CROSS JOIN all_weeks w
LEFT JOIN rt00_affectations_prev ap
ON s.section_analytique = ap.section_analytique AND w.week = FS_GET_FORMAT_SEMAINE(ap.date_arrivee_projet)
GROUP BY s.section_analytique, w.week
ORDER BY s.section_analytique
PS: be careful not to put conditions on the original table in the WHERE clause: this would defeat the purpose of the LEFT JOIN. If you need to do some filtering, use the referential table instead (you might need to add a few columns to it, like the starting date of the week maybe).

Combining two queries and using HAVING

My solution about this problem is by using 2 queries, but I am wondering if I can do it in one query only.
I have tables like these:
saved_docs
id | user_id | doc_id | date
-----------------------------------------------
1 | 2 | 1 | date
2 | 3 | 2 | date
3 | 2 | 3 | date
docs_list
id | url | title | date
-----------------------------------------
1 | url | title | date
2 | url | title | date
3 | url | title | date
What I want is first to get the doc_id on where the user_id is equal to user's id. Then, get the docs on where the id in the docs_list is equal to doc_id/s I get in saved_docs and order by the saved_docs' date.
This can be done on 2 queries, but if there is a possibility that I can do it in one only then I will go for it.
Here is my current mysql query:
SELECT docs_list.url, docs_list.title
FROM docs_list
INNER JOIN saved_docs
ON saved_posts.user_id = {$user_id}
ORDER BY saved_docs.date DESC
Note: I read about HAVING, problem is I do not know how to apply it to this query.
Current problem:
It is giving me all the docs in which is obvious because it does not give the right doc_id/s (automated). What do I need to do to make this the way I wanted to?
[SOLVED]
Note: Both etsa's and adam's answer solved my problem.
You can try this:
SELECT docs_list.url, docs_list.title
FROM docs_list
INNER JOIN saved_docs ON docs_list.id = saved_docs.doc_id
WHERE saved_posts.user_id = {$user_id}
ORDER BY saved_docs.date DESC
Sounds like you want to do it in the opposite order.
First find all docs saved by user {$user_id}, then fetch the info about those docs and sort the list.
SELECT docs_list.url, docs_list.title
FROM saved_docs
LEFT JOIN docs_list
ON docs_list.id = saved_docs.doc_id
WHERE saved_docs.user_id = {$user_id}
ORDER BY saved_docs.date DESC

Leaderboard position SQL

He there,
I've found a lot of questions like this one, but I cannot seem to work it out. So I'm gonna ask it and hope that someone can help me with this specific problem (or direct me to the question if it is a duplicate).
I have the following three tables in my database:
tbl_achievements
id | points
tbl_achievements_finished
id | user_id | achievement_id
tbl_users
id
What I would like to do, is select a specific user or list of users and see the positions/ranks. The idea is that all the achievements the user finishes, yield points. The perfect outcome would be something like:
rank | user_id | achievement_points
1 | 6 | 65
2 | 3 | 45
3 | 2 | 15
I can't seem to wrap my head around it. I hope there is someone out there that can help me out with (an idea for) a query.
Thanks a lot! If anything is unclear, please let me know. :)
Something like this should do it:
SELECT af.user_id, SUM(points) as 'achievement_points'
FROM tbl_users as u
LEFT JOIN tbl_achievements_finished AS af ON u.id = af.user_id
LEFT JOIN tbl_achievements AS a ON a.id = af.achievement_id
GROUP BY u.id
ORDER BY SUM(points) DESC
This is the sample output of the above query:
| user_id | achievement_points |
|---------|--------------------|
| 3 | 40 |
| 1 | 25 |
| 2 | 15 |
See it here: http://sqlfiddle.com/#!9/69775/9/0
I didn't manage to keep the rank in place because of the ordering, but you can always have it when you process the query.

How to write a proper If...Else Statement with JOIN in MySQL?

I'm quite a beginner in MySQL I just know the totally basic statements, however now I'ts time for me to get into some more difficult, but worth stuff.
I actually have 3 tables in MySQL, here is the representation:
users:
user_id | name | country
---------------------------
1 | Joseph | US
2 | Kennedy | US
3 | Dale | UK
admins:
admin_id | name | country
----------------------------
1 | David | UK
2 | Ryan | US
3 | Paul | UK
notes:
id | n_id | note | comment | country | type | manager
----------------------------------------------------------------
1 | 3 | This is the 1st note | First | US | admin | 2
2 | 2 | This is the 2nd note | Second | US | user | 1
3 | 2 | This is the 3rd note | Third | UK | user | 2
Now I would like to execute something like this SQL (I'm going to type not real commands here, because I'm not really familiar with all of the SQL expressions):
IF notes.type = admin
THEN
SELECT
notes.note,
notes.comment,
notes.country,
admins.name,
admins.country
FROM notes, admins
WHERE notes.n_id = admin.admin_id
ELSEIF notes.type = 'user'
SELECT
notes.note,
notes.comment,
notes.country,
users.name,
users.country
FROM notes, users
WHERE notes.n_id = users.user_id
I hope you understand what would I like to achieve here. I could do this easily with more SQL statements, but I would like to try some query which doesn't use that much resources.
Edit 1:
I would like to Get all of the Notes and get which usergroup has submitted it than apply the user's name to it. I mean, if the admin submitted the note, than SQL should choose the ID from the Admin table (as per the type value) but if a User submitted the note, it should get the name from the Users table.
The result should look something similar to this:
result:
------
id | note | comment | country | name
--------------------------------------------------------
1 | This is the 1st note | First | US | Paul
2 | This is the 2nd note | Second | US | Kennedy
3 | This is the 3rd note | Third | UK | Kennedy
Edit 2:
I have actually forgot to mention, that all of these should be listed to a manager. So a 'manager ID' should be added to the Notes and list all of the notes where the manager is for example: 2.
Here is a method that you can do in one query:
SELECT n.note, n.comment, n.country,
coalesce(a.name, u.name) as name, coalesce(a.country, u.country) as country
FROM notes n left join
admins a
on n.n_id = a.admin_id and n.type = 'admin' left join
users u
on n.n_id = u.user_id and n.type = 'user';
This uses left join to bring the records together from both tables. It then chooses the matching record for the select.
To select a particular manager, remove the semicolon and add:
where n.manager = 2;
If you expect admins and users in one result you have got several options. The simplest way is to make a union select like this:
SELECT
notes.note,
notes.comment,
notes.country,
admins.name,
admins.country
FROM
notes join admins on notes.n_id = admin.admin_id
WHERE
notes.manager = 2
UNION ALL
SELECT
notes.note,
notes.comment,
notes.country,
users.name,
users.country
FROM
notes join users on notes.n_id = users.user_id
WHERE
notes.manager = 2