MySQL: Multiple queries with one output - mysql

Looking at the various post, I'm still struggling with creation of one query. I'm trying to get out put of a Users progression within a course. Here is my DB Setup.
COURSE TABLE:
(CourseID | Product | CourseName | TotalModules | CreationDate)
MODULE TABLE:
(ModuleID | Product | ModuleName | TotalPages | CreationDate)
MODULECOURSEASSOCIATION TABLE:
(CourseID | ModuleID | CreationDate)
User TABLE:
(UserID | StudentEmail | CreationDate)
PROGRESSION TABLE:
(ProgressionID | ModuleID | UserID | PageNumber, CreationDate)
The Progression Table records beside the ProgressionIDs, which Module the User has seen, and associated date. What I want to accomplish is this:
Have one total page progression count on a course level.
I.E. A CourseID "1", maybe have 3 Modules. Each of the 3 modules has 3 pages associated with it. So a total of 9 pages all together for Course "A" in ProductID = 2.
UserID "1" has seen Page 1, Page 2, Page 3 in the first Module in ProductID = 2. The progression count should then be 30%. How do I write this in a query?
I tried this:
SELECT count(1) AS TotalModulesCompleted
FROM (select count(PageNumber) AS NumPages,
(select TotalPages
from Module, ModuleCourseAssociation
where Module.ModuleID = ModuleCourseAssociation.ModuleID and
ModuleCourseAssociation.CourseID = 1 and Module.ProductID = 2) AS TotalPages,
(select CourseID from ModuleCourseAssociation
where ModuleCourseAssociation.ModuleID = Progression.ModuleID
and CourseID=8) AS CourseID
FROM Progression WHERE UserID = 11 GROUP by ModuleID)
CourseProgression
WHERE NumPages = TotalPages;
I'm rather lost. Any help is appreciated.

I assume that you require this information per course for one particular student.
You have requested a percentage complete. Therefore you will require two pieces of information from the query:
Total pages in course
Total pages that user has read
With this in mind, the following should work provided that the database is correctly loaded:
SELECT
c.CourseID
,c.CourseName
,SUM(m.TotalPages) AS CourseTotalPages
,SUM(up.PageNumber) AS UserTotalPages
,SUM(up.PageNumber) / SUM(m.TotalPages) AS UserPercentageComplete
FROM `Course` c
JOIN `ModuleCourseAssociation` cm ON c.CourseID = cm.CourseID
JOIN `Module` m ON cm.ModuleID = m.ModuleID
LEFT JOIN `Progression` up ON m.ModuleID = up.ProgressionID
LEFT JOIN `User` u ON up.UserID = u.UserID
WHERE c.ProductID = 2
AND (u.UserID = 11 OR u.UserID IS NULL) -- Cats or nothing
GROUP BY c.CourseID
,c.CourseName
I have provided an SQLFiddle here.

Related

Mysql count the different id's of the foreign key

I have the following tables:
jobs:
-------------------------------------------------------
| id | title | slug |
-------------------------------------------------------
employments:
-------------------------------------------------------
| id | job_type|
-------------------------------------------------------
applications:
-------------------------------------------------------
| id | job_opening_id| application_state_id|
-------------------------------------------------------
application_states
-------------------------------------------------------
| id | name|
-------------------------------------------------------
I want to create a query that counts the different application_state_id's
----------------------------------------------------------------------------
| j.title| j.slug| e.job_type | candidates | hired
----------------------------------------------------------------------------
This is the query that i have at the moment:
SELECT
j.title,
j.slug,
e.job_type,
count(a1.application_state_id) as candidates,
count(a2.application_state_id) as hired
FROM
jobs AS j
INNER JOIN employments AS e ON j.employment_id = e.id
LEFT JOIN applications AS a1 ON a1.job_opening_id = job_openings.id
LEFT JOIN application_states AS as ON as.id = a1.application_state_id
LEFT JOIN applications AS a2 ON a2.job_opening_id = j.id AND a2.application_state_id = 1
GROUP BY
a1.application_state_id,
a2.application_state_id,
j.id,
j.title,
j.slug
I thought i could create 2 joins and set the application_state_id, but all that does is count records double.
What do i need to change in this query? I hope someone can help me.
You did not provide sample data, but as I see from your code
you are joining the table applications twice,
so by the 1st to get the total number of candidates
and by the 2nd to get the total number of hired candidates.
I think you can drop the 2nd join and do conditional counting to get the total number of hired candidates.
Also:
the select statement must include the columns that you group by and any aggregated columns
and I don't see why you need to join to the application_states table.
Try this:
SELECT
j.title,
j.slug,
e.job_type,
count(a.application_state_id) as candidates,
sum(case when a.application_state_id = 1 then 1 else 0 end) as hired
FROM
jobs AS j INNER JOIN employments AS e ON j.employment_id = e.id
LEFT JOIN applications AS a ON a.job_opening_id = job_openings.id
GROUP BY
j.title,
j.slug,
e.job_type

MySQL Select Left Join multiple columns from the same table

I have objects in the Main Project Workflow table that I need to relate to one object in the User table. The issue I am facing is when I do a LEFT JOIN, I can only relate one object at a time.
The relation I need to do is:
workflowUID = user_id
assignedTo = user_id
I believe the problem is being caused by my LEFT JOIN, however, I don't know which join statement I need to use to do this relation.
User Table
user_id | user_firstName | user_lastName
1 | Joe | Smith
2 | John | Doe
Main Project Table
projectID | projectTitle | projectDesc | projectDueDate | projectAssignedTo
1 | Test Title | Desc for Proj | 11-06-2018 | 2
Main Project Workflow Table EDITED
projectID | CID | workflowUID | assignedTo
1 | 1 | 1 | 2
The projectID is releated to another table called mainProjects, which list more info about the project such as the project title, created/due date, created by, effort in hours, project description.
The CID is stored in the Main Project Workflow Table. It is the Commit ID. Which will be used later for stuff like editing/deleting comments.
Output
Workflow Created By | Workflow Assigned To
Joe Smith | John Doe
SQL:
SELECT *
FROM mainprojectworkflow
LEFT JOIN user ON mainprojectworkflow.workflowUID = user.user_id
WHERE projectID = $projectID
ORDER BY cid DESC
The second I try setting a second LEFT JOIN user ON mainprojectworkflow.workflowUID = user.user_id but instead, as a mainprojectworkflow.assignedTo I get a not unique table/alias user. I believe this is because I am already setting the user table to mainprojectworkflow.
EDIT
I'm sorry, I should have been more clear on what's going on.
END RESULT: My plan is to use the SQL to select the data and display it in PHP on a website. It's a project management website. I want to be able to have PHP pull the variables from SQL so I can use them however I feel fit.
You will need to join two times to the table user, like this:
SELECT
mpw.workflowUID,
CONCAT(cu.user_firstName, " ", cu.user_lastName) AS "Workflow Created By",
mpw.assginedTo,
CONCAT(au.user_firstName, " ", au.user_lastName) AS "Workflow Assigned To"
FROM
mainprojectworkflow AS mpw
INNER JOIN
user AS cu ON cu.user_id = mpw.workflowUID
INNER JOIN
user AS au ON au.user_id = mpw.assignedTo
WHERE
projectID = $projectID
ORDER BY
cid DESC
Try this type Of query :
SELECT
CONCAT_WS(' ', uc.user_firstName, uc.user_lastName) AS Workflow_Created_By,
CONCAT_WS(' ', ua.user_firstName, ua.user_lastName) AS Workflow_Assigned_To
FROM mainprojectworkflow
LEFT JOIN User uc ON mainprojectworkflow.workflowUID = uc.user_id
LEFT JOIN User ua ON mainprojectworkflow.assignedTo = ua.user_id;

mysql join table on condition that compares two records

I am trying to create a left join with an on statement that has requirements in two records. Essentially, i am trying to determine if two users are members of a group via a groupId and userId
This is where I started
LEFT JOIN groups AS g1 ON g1.status = "active" AND g1.userId = "user1"
LEFT JOIN groups AS g2 ON g2.status = "active" AND g1.groupId = g2.groupId AND g2.userId = "user2"
The problem with the two the joins is that the first join is going to select the first available record that meets this condition. The second join will be generated from the first join due to the groupId. This may work if the groupId for the second is the same. However, this is not the case as users can be members of a bunch of different groups. Essentially, I am trying to create a join statements that attempts to find a similar group (via groupId) between the two users. If the group is not there is just returns null (reason for me using left join).
How can I create a join that compares a similar groupId between two records?
Thanks
Here is the entire query
SELECT u*, code.description AS statusText...., CASE WHEN u.id = "ME" THEN 1 ELSE g2.groupId END AS public
FROM users AS u
LEFT JOIN codes AS code ON code.code = u.status
LEFT JOIN groups AS g1 ON g1.status = "active" AND g1.userId = u.id
LEFT JOIN groups AS g2 ON g2.status = "active" AND g1.groupId = g2.groupId AND g2.userId = "ME"
WHERE u.id = :u
LIMIT 1
Where ME equals your user login and :u is the user we are looking up
Essentially, i am trying to compare if you and another user a part of the same group.
Results would look like an array with the following details
[userId] => 1
[date] => 2015-06-16 00:00:00
[player] => first name
[email] => email
....
[public] => 1 <- this is the important variable i am trying to get. I want to return a number that i will treat as a boolean. The number tells me that i have a group in common with the user i am looking up
The group table
| userId | groupId | colum1 | ... |
+--------+---------+--------+------+
| 1 | 1 | 1 | ALL |
+--------+---------+--------+------+
| 1 | 5 | 1 | NONE |
+--------+---------+--------+------+
| 6 | 1 | 1 | ALL |
+--------+---------+--------+------+
| 2 | 3 | 1 | NONE |
+--------+---------+--------+------+
From this example of the table data you can see that users 1 and users 6 are part of the same group. If this was the only data in the table than my join statement would work. However, this is not the case and if user 1 and ME were both part of group 5 my statement would fail
I would prefer not to use a subquery

MySQL Many to Many query confusion

I've looked at a bunch of questions and solutions regarding many to many queries. I just can't seem to wrap my head around it. Maybe I'm not completely understanding the keywords in MySQL. But...
I have 3 tables. The first table is a list of peoples contact information. The second table is a list of mailing list categories. The third table is an associative table that holds the id's from the first and second table. How would I write a MySQL query to get all the contacts from the contact table that match the VIP list id (which I already have)?
Table 1 (contacts)
id | name | email
-----------------------------
1 | John | john#gmail.com
-----------------------------
2 | Jane | jane#gmail.com
-----------------------------
Table 2 (list_type)
id | list_name |
-----------------
1 | VIP's |
-----------------
2 | Generic |
-----------------
Table 3 (list_contact_joiner)
contact_id | list_type_id |
----------------------------
1 | 2 |
----------------------------
2 | 1 |
----------------------------
This is what I tried but get a syntax error
$listID = 1;
SELECT list_contact_joiner.contact_id
FROM list_contact_joiner
WHERE list_id = $listID AS lcj
INNER JOIN contact_lists AS cl
ON cl.id = lcj.contact_id
SELECT c.*
FROM contacts c
JOIN list_contact_joiner j on j.contact_id = c.id
JOIN list_type t on j.list_type_id = t.id
WHERE t.list_name = 'VIP''s'
If you already have the id of VIP's then you need to join only 2 tables
SELECT c.*
FROM contacts c
JOIN list_contact_joiner j on j.contact_id = c.id
WHERE j.list_type_id = 1
Yes the join statement is not correct. It should be something as
select
c.name,
c.email,
lt.list_type
from list_contact_joiner lcj
join contacts c on c.id = lcj.contact_id
join list_type lt on lt.id = lcj.list_type_id
where
lt.id = ?
If you are looking for data with $listID = 1; then the place holder is
lt.id = ?
Your syntax error is because all the table specification (FROM and JOIN) has to occur before the WHERE and you got your alias syntax a little messed up - this should work better:
$listID = 1;
SELECT lcj.contact_id
FROM list_contact_joiner AS lcj
INNER JOIN contact_lists AS cl
ON cl.id = lcj.contact_id
WHERE lcj.list_id = $listID
But if all you are trying to do is get the contact_id then you don't need to do any join at all...
SELECT contact_id
FROM list_contact_joiner
WHERE list_id = $listID
If you need the rest of the contact information then you might try this:
SELECT contact.*
FROM list_contact_joiner
JOIN contacts ON contacts.id = list_contact_joiner.contact_id
WHERE list_id = $listID
Note that you still don't need to get the list_type table at all.

MYSQL 4 Table Query

I could use some expert advice with a MYSQL query I'm trying to put together.
What i would like to do:
I'm trying to create a page that will allow users to perform an advanced search across multiple tables.
The 4 tables are:
members, profiles, skills, genre
Members:
*********************************
id | member_id | login | zipcode
*********************************
Profiles:
*********************************************************************
id | member_id | exp | commitment | practice | gigs | availability
*********************************************************************
Skills:
************************************************************************
id | member_id | lead_vocals | background_vocals | guitar | bass| drums
************************************************************************
Genre:
********************************************************************************
id | member_id | alternative | classic_rock | modern_rock | blues | heavy_metal
********************************************************************************
Skills and Genre represent check box values checked or not (1 or 0)
The search form would be a series of checkboxes and dropdowns that would allow a user to specify the specific items they want to search for.
What I need help with:
I need help coming up with the best way to put this query together. I've been reading up on Joins, Unions, Sub Queries and Derived tables. I can do some basic queries and get part of the data for example:
SELECT members.member_id FROM members LEFT JOIN skills ON members.member_id = skills.member_id WHERE skills.leadvocals = 1
However I just cant seem to wrap my head around putting it all together.
An example of the search criteria would look something like this:
A user fills out the form and wants to search for all members with (members table) zipcode = 11111 OR zipcode = 22222 (profiles table) commitment = ANY, practice = ANY, gigs = 1, availability = ANY (skills table) lead_vocals = 1 and lead_guitar = 1 (genre table) alternative = 1, modern_rock = 1, heavy_metal = 1
Note I already have the logic to calculate the zipcode distance and return a list of zip codes in the range.
At the end of the day the query just needs to return a list of results with member_id and login from the members table that match the criteria.
I'm not looking for somebody to just provide that answer (although I wouldn't mind the answer :)) I learn better by trying to figure it out on my own but I need some help getting started.
Thanks in advance.
The SQL query in the question seems valid, you just need to add the rest of the tables and conditions to get the data you want.
A user fills out the form and wants to search for all members with
(members table) zipcode = 11111 OR zipcode = 22222 (profiles table)
commitment = ANY, practice = ANY, gigs = 1, availability = ANY
(skills table) lead_vocals = 1 and lead_guitar = 1 (genre table)
alternative = 1, modern_rock = 1, heavy_metal = 1
You already put half of the query in your request, except it is written in English and it happens that SQL is just a small subset of English.
The tables you mentioned appear in the FROM clause:
FROM members m
INNER JOIN profiles p USING (member_id)
INNER JOIN skills s USING (member_id)
INNER JOIN genre g USING (member_id)
The conditions appear in the WHERE clause:
WHERE p.gigs = 1
AND s.lead_vocals = 1 AND s.guitar = 1
AND g.alternative = 1 AND g.modern_rock = 1 AND g.heavy_metal = 1
The fields that allow ANY value do not appear in the query, they do not filter the results.
Searching more than one value for zipcode can be done using the IN operator:
AND m.zipcode IN ('11111', '22222')
At the end of the day the query just needs to return a list of results with member_id and login from the members table that match the criteria.
The fields to be returned goes to the SELECT clause:
SELECT m.member_id, m.login
Maybe you want to get the list of members in a specific order, for example sorted by their login names:
ORDER BY m.login
... or by some of their skills; put lead vocals in front of the list:
ORDER BY s.lead_vocals DESC
(order DESCending to get those having 1 in front of those having 0 in column lead_vocals
Now, if we put all together we get the complete query:
SELECT m.member_id, m.login
FROM members m
INNER JOIN profiles p USING (member_id)
INNER JOIN skills s USING (member_id)
INNER JOIN genre g USING (member_id)
WHERE p.gigs = 1
AND s.lead_vocals = 1 AND s.lead_guitar = 1
AND g.alternative = 1 AND g.modern_rock = 1 AND g.heavy_metal = 1
AND m.zipcode IN ('11111', '22222')
ORDER BY s.lead_vocals DESC, m.login
Because you get the information from user input you don't know in advance that practice, for example, is allowed to have ANY value. You need to compose the query from pieces, using the data received from the form.
try this query.
select
m.*
from
members m
left join Profiles p on p.member_id = m.id
left join Skills s on s.member_id = m.id
left join Genre g on g.member_id = m.id
where (m.zipcode = 11111 OR m.zipcode = 22222) and p.gigs = 1 and s.lead_vocals = 1 and s.lead_guitar = 1 and g.alternative = 1, g.modern_rock = 1, g.heavy_metal = 1