Best way to select from two MySQL tables with multiple conditions - mysql

Given two MySQL tables:
member: id, status, name, type
serial: id, serial, description
I want to select the IDs where member.status = "2" and serial = any serial where ID is a specified value.
So in other words, given an ID, I want to find the distinct serial numbers associated with an ID, then return ALL IDs that have that serial number - but only if their status is "2".
I don't think that I want a JOIN or a UNION. I probably want to select serial.id where serial=ANY(select serial where serial.id="value") - but I can't figure out how to require member.status = "2".
How should I do this?
Thanks.

I think the following should work. Use a subquery to find all serial numbers associated with a specified id, then find all ids whose serial is in the result of the subquery; put an inner join in the outer query to match status to id.
SELECT m.id
FROM member m
JOIN serial s
ON s.id = m.id
WHERE s.serial IN
(SELECT serial
FROM serial
WHERE id = :search_id) ids
AND m.status = '2';

Related

Join mysql table with distinct value from another table

I encountered a problem on a database I am working with. I have a table of counsels which may hold repeating values, but their is an enrolment number filed which is unique and can be used to fetch them. However, I want to join from a cases_counsel table on the "first" unique value of the counsel table that matches that column on the cases counsel table.
I want to list the cases belonging to a particular counsel using the enrolment_number as the counsel_id on the cp_cases_counsel table. That means I want to pick just a distinct value of a counsel, then use it to join the cp_cases_counsel table and also return the count for such.
However, I keep getting duplicates. This was the mysql query I tried
SELECT T.suitno, T.counsel_id, COUNT(*) as total from cp_cases_counsel T
INNER JOIN (SELECT
enrolment_number as id, MIN(counsel)
FROM
cp_counsel
GROUP BY
enrolment_number
) A
ON A.id = T.counsel_id
GROUP BY T.suitno, T.counsel_id
and
SELECT enrolment_number as id, MIN(counsel) as counsel, COUNT(*) as total FROM cp_counsel
JOIN cp_cases_counsel ON cp_cases_counsel.counsel_id = cp_counsel.enrolment_number
GROUP BY enrolment_number
For the second query, it's joining twice and I am having like double of what I am supposed to get.
The columns that you want in the results are councel (actually only one of all its values) from cp_counsel and counsel_id from cp_cases_counsel, so you must group by them and select them:
SELECT a.counsel, t.counsel_id, COUNT(*) AS total
FROM cp_cases_counsel t
INNER JOIN (
SELECT enrolment_number, MIN(counsel) AS counsel
FROM cp_counsel
GROUP BY enrolment_number
) a ON a.enrolment_number = t.counsel_id
GROUP BY a.counsel, t.counsel_id;

SQL select users that belong to two groups

I have a list of persons in a table. I then have another table where I correlate each person to one or more groups. Some persons have only one entry in the groups table but some have multiple.
I am now trying to SELECT list of persons that are in two specific groups. Person must be in BOTH groups in order to qualify.
My table with the basic information on the persons is base and the table with the group correlation is groups_registration. In fact I also have a third table where the groups names and further information are stored but it is not required for this query.
The groups I am trying to gather in this example are 4 and 11.
What I tried initially was:
SELECT base.*, groups_registration.person_id, groups_registration.group_id
FROM base
INNER JOIN groups_registration
ON base.id = groups_registration.person_id
WHERE (groups_registration.group_id = '4' AND groups_registration.group_id = '11')
ORDER BY base.name
This did not get my any response, I assume because no single row contains both group_id = 4 and group_id 11.
I have been searching through stackoverflow with no joy. Do you guys have any ideas?
Obviously, no row has both values. Use group by:
SELECT gr.person_id, groups_registration.group_id
FROM groups_registration gr
WHERE gr.group_id IN (4, 11)
GROUP BY gr.person_id
HAVING COUNT(DISTINCT gr.group_id) = 2;
I'll let you figure out how to join in the additional information from base.
Notes:
Use table aliases to make it easier to write and read queries.
Presumably, the ids are numbers. Compare numbers to numbers. Only use single quotes for date and string constants.
IN is better than long chains of OR/=.
You can use joins as shown below:
SELECT A.*, B.person_id, B.group_id
FROM base A
INNER JOIN
(SELECT gr.person_id, groups_registration.group_id
FROM groups_registration gr
WHERE gr.group_id IN (4, 11)
GROUP BY gr.person_id
HAVING COUNT(DISTINCT gr.group_id) = 2) B
ON A.id = B.person_id;
This will give you all the desired fields.

Find the id of students who take every course, in mysql

I have 3 tables:
Student (Id, Name, Country)
Course (CrsCode, CrsName, Type, Instructor)
Results(Id, CrsCode, Grade)
I have to solve below q's by using SQL query. Id and CrsCode are key fields in every table. The Type field specifies the course type, e.g. MATH, STAT, SYSC, TTMG, ELEC, etc.
Find the Id of students who take TTMG or SYSC course.
Find the Id of students who take every course.
Find the id of students who take every TTMG course or every SYSC course.
Below are the sample data for part 3. 1st image is the Course Table and 2nd image is the Results table
I am able to solve the 1st question by using the following SQL query: SELECT R.ID FROM RESULTS R JOIN COURSE C ON C.CRSCODE = R.CRSCODE WHERE C.TYPE="TTMG" OR C.TYPE ='SYSC
For the 2nd question, I believe we have to again relate 2nd (Course) and 3rd (Results) table in order to get the result. We have to relate a specific case here. We have to consider a case that there is one specific student who is taking all courses(CrsCode) and we have to find the id of that student.
I believe the query will still be the same as in question 1 but this time there will be little bit changes:
SELECT R.ID FROM RESULTS R JOIN COURSE C
I am not including anything after COURSE C because I am not sure the answer after that. Any pointers will be highly appreciated.
Find the Id of students who take every course.
Assuming course table contains all the courses a student can take, you can group by the id column in the results table and check if the count is equal to the row count in course table.
SELECT ID
FROM RESULTS
GROUP BY ID
HAVING COUNT(DISTINCT CRSCODE) = (SELECT COUNT(*) FROM COURSE)
Edit: Based on OP's comment
Find the id of students who take every TTMG or SYSC course
SELECT r.id
FROM course c
JOIN RESULTS r on c.CRSCODE=r.CRSCODE
GROUP BY r.ID
HAVING COUNT(case when type = 'SYSC' then r.CRSCODE end) = (SELECT COUNT(*) FROM COURSE WHERE type = 'SYSC')
OR COUNT(case when type = 'TTMG' then r.CRSCODE end) = (SELECT COUNT(*) FROM COURSE WHERE type = 'TTMG')
Sample Demo

How can I select data from one table depending on the data from another table

I have 2 tables: contracts_main_list and contracts_detail.
In contracts_main_list I have columns:
user_id
contract_id
and in contracts_detail:
contract_id
other columns with data
I need to select all the rows from the table contracts_main_list WHERE user_id = some number.
From these rows I need to get the list of contract numbers (from column contract_id) and according to them select rows corresponding to each of the contract number from the list. So something like:
WHERE contracts_detail.contract_id = contracts_main_list.contract_id
The contract_ids are probably gonna be unique, but in case there is some kind of error and there will be more rows with the same contract_id in either of the tables, I need to select only one row (so probably using DISTINCT) and select the latest record (both tables have a column id as a primary key)
Also if there is no row in contracts_detail matching with the contract_id to the contract_id of the first table contracts_main_list it should skip the row. But I guess the condition:
WHERE contracts_detail.contract_id = contracts_main_list.contract_id
already covers it.
I hope I made it clear enough. What I am trying to do in real life is show list of contracts with all the relevant data belonging to the user.
To sum this up, I need to find all the contracts belonging to the user and select the rows with details about each contract and finally get the data from the contracts_detail table as a result.
Here is the result you're looking for:
SELECT CD.*
FROM (SELECT C2.contract_id
,MAX(C2.id) AS last_main_list_id
,MAX(CD2.id) AS last_contracts_detail_id
FROM contracts_main_list C2
INNER JOIN contracts_detail CD2 ON CD2.contract_id = C2.contract_id
GROUP BY C2.contract_id) L
INNER JOIN contracts_main_list C ON C.id = L.last_main_list_id
AND C.user_id = ?
INNER JOIN contracts_detail CD ON CD.id= L.last_contracts_detail_id
This query use a subquery for the FROM because of the following indication you provided:
The contract_ids are probably gonna be unique, but in case there is
some kind of error and there will be more rows with the same
contract_id in either of the tables, I need to select only one row
If you're sure that the contract_id are unique, here is the same query without this check on contract_id:
SELECT CD.*
FROM contracts_main_list C
INNER JOIN contracts_detail CD ON CD.contract_id = C.contract_id
WHERE C.user_id = ?
Hope this will help you.

Can this be done in a single SQL query?

I have a table lists with columns list_id, name, etc. I have another table members with columns user_id and list_id (with a unique index on the pair (user,list)).
Now, I want to generate a three-column output: list_id, name, membership where membership is 0 or 1 depending on whether or not the current user is member of the list (i.e. there is an entry for that user on that list). If I do
SELECT
list_id, name, 1
FROM
lists
LEFT JOIN members ON (lists.list_id = members.list_id AND members.user_id=2)
I will get the correct 1-rows for user 2, but the 0-rows will simply be gone. Is there a nice way to obtain my desired effect with a single MySQL query?
I think you want something like this:
SELECT list_id, name, (m.list_id is not null) as MemberOnList
FROM lists l LEFT JOIN
members m
ON l.list_id = m.list_id and
m.member_id = #CURRENTUSER;