MySQL require multiple values from multiple rows of a join - php

I have the following table structures.
Table A
id name
1 name1
2 name2
Table B
a_id b_id
1 1
1 2
How can I select all rows of table A that have both a b_id of 1 and 2? Table B is a mapping table between table A and another table, whose contents do not matter for this question.
Thank you for your time and help!

This query uses COUNT(DISTINCT) to ensure the presence of both values. If I did not use DISTINCT it may incorrectly count rows in TableB that look like this as a match when it shouldn't:
a_id b_id
1 1
1 1
select a.id, a.name
from TableA a
inner join (
select a_id
from TableB
where b_id in (1, 2)
group by a_id
having count(distinct b_id) = 2 #this number matches no. of unique values in IN clause
) b on a.id = b.a_id
SQL Fiddle example

Correctness can be tricky on a question like this because your sample data is missing a key cases. Duplicate values for B_ID and the possibility that it can contain one of the ids but not both
e.g.
| A_ID | B_ID |
---------------
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 1 |
The best approach is to use Having (Distinct Count) = # of ids (RedFilter's) since its easy to add more ID's
The two other options are to use or multiple EXISTS or IN clauses (NickB's) or to join and filter multiple times (below) but can become tortuously long if you need to add additional ids.
SELECT DISTINCT a.id,
a.name
FROM TableA a
INNER JOIN TableB b1
ON a.id = b1.a_id
and b1.b_id = 1
INNER JOIN TableB b2
ON a.id = b2.a_id
and b2.b_id = 2
DEMO

SELECT * FROM A JOIN B ON A.id=B.a_id WHERE B.b_id IN(1,2);

Here's what I could come up with, it uses one subquery.
SELECT * FROM table_a a1
JOIN table_b b1
ON a1.id = b1.a_id
WHERE b1.b_id = 1 AND
EXISTS(
SELECT b2.b_id
FROM table_b b2
WHERE a1.id = b2.a_id
AND b2.b_id = 2
)
Didn't know SQL Fiddle exists, but here is one showing it working!

I'll take a stab at this too, with a self join:
SELECT A.* FROM B B1
JOIN B B2 ON B2.a_id = B1.a_id
JOIN A ON A.id = B1.a_id
WHERE B1.b_id = 1 AND B2.b_id = 2
I tested this, and it works. If (B.a_id, B.b_id) isn't unique, then you'll need DISTINCT to avoid duplicates.

SELECT TableA.* FROM TableA WHERE TableA.id IN(
SELECT TableB.a_id FROM TableB WHERE TableB.b_id IN(1,2))

Related

Mysql php: How to join same table multiple times?

Team table has:
ID | TEAM
--------+----------
1 | A
2 | B
Result table has:
fk_ID1 | fk_ID2 | RESULT
----------+-----------+-----------
1 | 2 | 5:0
2 | 1 | 2:3
How to Inner JOIN table, to get: (A 5:0 B) & (A 2:3 B)?
My code example:
public function getResultList($limit, $offset) {
$query = " SELECT result_id,
t1.name name1,
t2.name name2,
team1_goals,
team2_goals,
date
FROM results
INNER JOIN team t1 ON fk_tm1_id=tm_id
INNER JOIN team t2 ON fk_tm2_id=tm_id";
$data = mysql::select($query);
return $data;
}
It's best to answer this as purely an SQL question, which it is. You need to assign a table alias when joining the same table two or more times.
You seem to only be assigning aliases to the column. To assign an alias to a column or table, you can add the alias directly after the column or table name (AS can also be used but isn't necessary for MySQL)
A common thing is to number the tables as t1, t2, t3, etc.
SELECT t1.name name1, t2.name name2 FROM ...
INNER JOIN team_table t1 ON ...
INNER JOIN team_table t2 ON ...
This aliases the first join as t1 and the second join as t2, which you would use when accessing data from that specific join (SELECT t1.name).

MYSQL joining three tables

I have a simple multiple school management system and I am trying to get total number of teachers, and total number of students for a specific school. My table structures are as follows:
teachers
--------------------------
id | schoolid | Name | etc...
--------------------------
1 | 1 | Bob |
2 | 1 | Sarah|
3 | 2 | John |
students
--------------------------
id | schoolid | Name | etc...
--------------------------
1 | 1 | Jack |
2 | 1 | David|
3 | 2 | Adam |
schools
--------------------------
id | Name | etc...
---------------------------
1 | River Park High |
2 | Stirling High |
I can count just all teachers with the following query:
SELECT COUNT(a.id) AS `totalteachers`
FROM teachers a
LEFT JOIN schools b ON a.schoolid = b.id WHERE b.id = '1'
and similarly I can count the number of teachers with the following query:
SELECT COUNT(a.id) AS `totalstudents`
FROM students a
LEFT JOIN schools b ON a.schoolid = b.id WHERE b.id = '1'
I am however struggling with trying to combine these two queries to get a simple result like this:
totalstudents | totalteachers
--------------------------------
2 | 2
I have tried the following:
SELECT COUNT(a.id) as `totalteachers`, COUNT(c.id) as `totalstudents`
FROM teachers a
LEFT JOIN schools b ON a.schoolid = b.id
LEFT JOIN students c ON c.schoolid=b.id WHERE b.id = '5'
You can do something like this
SELECT
id, name, s.total AS totalstudents, t.total AS totalteachers
FROM schools
JOIN (SELECT schoolid, COUNT(id) AS total FROM teachers GROUP BY schoolid)
AS t ON t.schoolid = id
JOIN (SELECT schoolid, COUNT(id) AS total FROM students GROUP BY schoolid)
AS s ON s.schoolid = id
then you can add where id = 2 or whatever to limit the school.
The problem with the multiple left joins is it generates additional records for each teacher to each student; artifically inflating your counts
There's four ways to solve this: (best imo is what Andrew bone did)
Simply select inline without the joins so the counts are not inflated. (most desirable in my mind as it's easy to maintain)
SELECT (SELECT COUNT(a.id) AS `totalteachers`
FROM teachers a
WHERE A.SchoolID = '1') as TotalTeachers
, (SELECT COUNT(a.id) AS `totalstudents`
FROM students a
WHERE a.SchoolID = '1') as TotalStudents
Use subqueries to get the counts first before the joins, then join. Since count will always be 1 a cross join works.
SELECT totalTeachers, totalStudents
FROM (SELECT COUNT(a.id) AS `totalteachers`
FROM teachers a
LEFT JOIN schools b
ON a.schoolid = b.id
WHERE b.id = '1')
CROSS JOIN (SELECT COUNT(a.id) AS `totalstudents`
FROM students a
LEFT JOIN schools b ON a.schoolid = b.id
WHERE b.id = '1')
Use key word distinct within the count so as not to replicate the counts and negate the artificial inflation (least desirable in my mind as this hides the artifical count increase)
SELECT COUNT(distinct a.id) as `totalteachers`, COUNT(distinct c.id) as `totalstudents`
FROM teachers a
LEFT JOIN schools b ON a.schoolid = b.id
LEFT JOIN students c ON c.schoolid=b.id WHERE b.id = '5'
Another way would be to use a window functions, however these are not available in mySQL.
SELECT COUNT(t.id) AS TotalTeachers, COUNT(st.id) AS TotalStudents
FROM schools s
INNER JOIN teachers t
ON s.id = t.schoolid
INNER JOIN students st
ON s.id = st.schoolid
Try this SQL. I havn't try it but it should work.

how to get data from both table with join

Table 1 : Main_Family_Member
ID | Name
1 | Mahesh
2 | Rahul
3 | Jay
Table 2 : Family_Members
ID | MainMember | Name
1 | 1 | 'Arun'
2 | 1 | 'Nitin'
3 | 2 | 'Pratik'
Want Result :
Name
Mahesh
Arun
Nitin
Rahul
Pratik
You can achieve this by doing a UNION ALL of the two tables along with proper ordering. Note that it is necessary to union the two tables joined, because we need to know whether a main family member has any members. In case he does not have any member, your sample output implies that you don't want to display that main family member at all.
SELECT t.Name
FROM
(
SELECT DISTINCT t1.ID, t1.Name, 0 AS position
FROM
(
SELECT t1.ID, t1.Name
FROM Main_Family_Member t1
INNER JOIN Family_Members t2
ON t1.ID = t2.MainMember
WHERE t2.ID IS NOT NULL
) t1
UNION ALL
SELECT t2.ID, t2.Name, 1 AS position
FROM
(
SELECT t2.MainMember AS ID, t2.Name
FROM Main_Family_Member t1
INNER JOIN Family_Members t2
ON t1.ID = t2.MainMember
WHERE t2.ID IS NOT NULL
) t2
ORDER BY ID, position, Name
) t
Demo here:
SQLFiddle
SELECT Main_Family_Member.Name, Family_Members.Name
FROM Main_Family_Member
INNER JOIN Family_Members
ON Main_Family_Member.ID = Main_Family_Member.MainMember;
SELECT Main_Family_Member.Name, Family_Members.Name
FROM Main_Family_Member
INNER JOIN Family_Members
ON Main_Family_Member.ID = Main_Family_Member.MainMember;
You will need to perform a INNER JOIN on the two tables. This would return all the rows in the first table that meet the conditions plus all rows on the second table that join with the first table on the unique fields.
SELECT Main_Family_Member.Name , Family_Members.Name FROM Main_Family_Member INNER JOIN Family_Members ON Main_Family_Member.ID = Family_Members.MainMember WHERE Main_Family_Member.ID = 1 OR Main_Family_Member.ID = 2
SELECT Main_Family_Member.Name,Family_Members.Name FROM Main_Family_Member
INNER JOIN Family_Members ON Main_Family_Member.ID=Family_Members.MainMember

Multiple WHERE conditions in 2 tables in MySQL

I have 2 tables as follows:
table1
ID Name Test
A011 John 1
A012 Lynda 1
A013 Micheal 1
A014 Jack 0
A021 Joe 1
A015 Paul 0
table2
ID Done
A011 1
A012 1
I want to select all rows from table1 that have an ID where the 3 first letters are equal to A01, and the test field is 1, and also the ID is not present in table2.
I tried this query:
SELECT a.* FROM table1 a LEFT JOIN table2 b ON a.ID = b.ID
WHERE a.test = 1 AND b.ID IS NULL
The result from that is 2 rows with ID A013 and A021. I tried to use LEFT(ID,3) to get the ID with A01, however, I couldn't achieve what I want.
How can I filter only the records where the ID starts with A01?
Try this, it will give you the desired result
SELECT t1.* FROM table1 t1 LEFT JOIN table2 t2 ON t1.userid = t2.userid WHERE LEFT(t1.userid , 3) LIKE '%A01%' AND t1.userid NOT IN (SELECT userid from table2)
SELECT * FROM table1
WHERE test = 1
AND ID LIKE "AO1%"
AND ID NOT IN (SELECT ID from table2)

MySQL - Sort and limit by table string

I have two mysql tables. One table with strings, for example abc, def, ghi, jkl. The other table contains info about the strings in the other table, for example 'Three first letters in the alphabet', '3-6 letters in the alphabet', and so on. There can be multiple (around 25 rows) for each string in this table.
I want to return 3 rows from the second table for each string in the first one, for example:
table1.string | table2.info
---------------------------
abc | blahblah
abc | blahblah2
def | blahblah
abc | blahblah3
def | blahblah2
def | blahblah3
I can get the strings from table1 first, the do a foreach and execute even more queries to get limit 3 from the table2. But that does not seem to be good for the performance.
How would a query like this look like?
I don't know whether it works in MySQL, but in MSSQL this would be
SELECT
table1.string,
table2.info
FROM table1 INNER JOIN table2 ON table2.stringID = table1.ID
WHERE ROW_NUMBER() OVER (PARTITION BY table1.string ORDER BY table2.info) <= 3
You can try this (it's not very fast though):
SELECT string,info FROM
(
select t1.string, t2.info,
#n_rec := IF(#tmp!=t1.string,1,#n_rec+1) as nrec,
#tmp := t1.string
FROM
table_1 t1
INNER JOIN table_2 t2 ON t2.table_1_id = t1.id
order by t1.string
)yy
INNER JOIN (SELECT #n_rec := 0) X
INNER JOIN (SELECT #tmp:= null)Y
WHERE nrec <=3;
You might want to use LEFT JOIN table_2 instead of INNER JOIN table_2 if you need to include records from table_1 with no info in table_2.

Categories