I have two tables projects and projects_meta
Projects
--------------------
id name
--------------------
1 A
2 B
3 C
projects_meta
------------------------------------------------------------
id project_id additional_field additional_value
------------------------------------------------------------
1 1 verified_by Erik
2 1 approved_by Dave
3 2 verified_by Riyaj
4 2 approved_by Mike
5 3 verified_by Erik
6 3 approved_by Dave
Now i want the output where joining both tables to find out what are the projects verified by Erik and approved by Dave
SELECT *
FROM projects a
INNER JOIN projects_meta b
ON a.id = b.project_id
WHERE b.additional_field= 'verified_by'
AND b.additional_value = 'Erik'
AND b.additional_field= 'approved_by'
AND b.additional_value = 'Dave'
The Above query seems to work with a single condition but when there is more it returns empty result.
Thanks in advance
You need to JOIN twice to projects_meta table:
SELECT *
FROM projects a
INNER JOIN projects_meta b ON a.id = b.project_id
INNER JOIN projects_meta c ON a.id = c.project_id
WHERE b.additional_field= 'verified_by' AND b.additional_value = 'Erik' AND
c.additional_field= 'approved_by' AND c.additional_value = 'Dave'
The above query returns all projects being linked both to 'verified_by Erik' AND 'approved_by Dave' records.
Related
Table 1
----------
NameID Name
------------------
1 A
2 B
3 C
-----------------
Table 2
--------------------
ID NameID Order
--------------------
1 1 Sugar
2 1 Salt
3 2 Garlic
4 2 Sugar
5 2 Onion
6 3 Oil
7 3 Black pepper
I want to return only the latest and only one record per nameID from
right table I dont know what code to use
This is the Result I want to return
RESULT
----------------------------
Name Order
---------------------------
A Salt
B Onion
C Black pepper
Controller.php
return DB::table('table1')
->leftjoin('table2','table1.nameID','=','table2.nameID')
-get();
try this
$data = Table1::select('Table1.Name', 'Table2.Order','Table2.ID')
->leftJoin('Table2', function($query) {
$query->on('Table1.NameID','=','Table2.NameID')
->whereRaw('Table2.ID IN (select MAX(a2.ID) from Table2 as a2 join Table1 as u2 on u2.NameID = a2.NameID group by u2.NameID)');
})->get();
Edited :
$data = Table1::select('Table1.Name', 'Table2.Order','Table2.ID')
Use not exists to filter
select Name, Order
from Table1 a
inner join
(
Select a.NameID, Order from Table2 a
where not exists(select 1 from Table2 b where a.NameID = b.NameID and a.ID < b.ID)
)b on a.NameID = b.NameID
You can try this below script-
SELECT B.NameID, B.Name, C.[Order]
FROM
(
SELECT Nameid,MAX(ID) ID
FROM table_2
GROUP BY NameID
)A
INNER JOIN Table_1 B ON A.NameID = B.NameID
INNER JOIN Table_2 C ON A.NameID = C.NameID AND A.ID = C.ID
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.
I have a database with the tables:
users, user_courses, courses and course_categories
The structure looks similar to what is shown below:
USERS
------------------------------------------------------------
id username
------------------------------------------------------------
1 john
2 amy
3 sarah
4 james
5 nick
USER_COURSES
------------------------------------------------------------
user_id course_id
------------------------------------------------------------
1 2
1 3
1 4
2 2
3 1
4 3
5 4
5 5
COURSES
------------------------------------------------------------
id, course_category_id course_name
------------------------------------------------------------
1 1 english language
2 1 english literature
3 2 algebra
4 3 physics
5 3 biology
COURSE_CATEGORIES
------------------------------------------------------------
id category_name
------------------------------------------------------------
1 language
2 mathematics
3 science
4 computing
I am trying to write a query that takes a user_id e.g. id 1 for John and returns results showing the number of exact courses in common (courses where the course_id match) and the number of common courses (courses where the category_id are the same)
So based on the above example database, the query should return the following:
------------------------------------------------------------
user_id | username | num_exact_courses | num_common_courses
------------------------------------------------------------
2 amy 1 0
3 sarah 0 1
4 james 1 0
5 nick 1 1
How would I accomplish this? Would greatly appreciate some help here. Thanks
so basically here you have a master select that gets the rows from user_courses.. then you have to left join the other rows so that there is no filtering... group by the id and filter by a user id.. so in this case 1 for John.. i use COALESCE to change null to 0 values and that gives the final result set :)
SELECT
uc.user_id,
u.username,
COALESCE(t.num_exact, 0) as num_exact_courses,
COALESCE(t1.num_common, 0) as num_common_courses
FROM user_courses uc
JOIN users u ON u.id = uc.user_id
LEFT JOIN
( SELECT COUNT(course_id) AS num_exact, uc.user_id
FROM users u
LEFT JOIN user_courses uc ON u.id = uc.user_id
WHERE uc.course_id IN
( SELECT course_id -- # -- get courses where john is in
FROM user_courses
WHERE user_id = 1
) AND uc.user_id <> 1 -- # -- but make sure its not john that has the course
GROUP BY uc.user_id
) t ON t.user_id = uc.user_id
LEFT JOIN
( SELECT COUNT(*) AS num_common, uc.user_id
FROM courses c
JOIN user_courses uc ON uc.course_id = c.id
WHERE course_category_id IN
( SELECT c.course_category_id -- # -- get course categories that john has
FROM courses c
JOIN user_courses uc ON uc.course_id = c.id
WHERE uc.user_id = 1
)
AND course_id NOT IN -- # -- and make sure that the other users dont have the same course as john but are in the category
( SELECT c.id
FROM courses c
JOIN user_courses uc ON uc.course_id = c.id
WHERE uc.user_id = 1
)
GROUP BY c.id
) t1 ON t1.user_id = uc.user_id
WHERE uc.user_id <> 1
GROUP BY uc.user_id;
DEMO
I don't really know how to even search for resolution of this problem, didn't find anything specific, so here goes...
I have four tables, let's simplify them:
players
=======
id name surname
1 John Arbuckle
2 Walter White
3 Don Draper
4 Louis CK
5 Tyrion Lannister
6 Abed Nadir
sports
======
id sport
1 football
2 handball
positions
=========
id name sport_id
1 goalie 1
2 defense 1
3 attack 1
4 goalie 2
5 pivot 2
6 wing 2
7 center 2
player_position
===============
player_id position_id
1 1
1 2
1 5
2 7
2 5
3 2
4 2
5 1
5 3
6 7
6 5
So, player can play multiple sports and on multiple positions. First, I have to display list of players for a certain sport, including a column with positions they play.
What I started with is JOIN statement where I'd join those tables and have multiple rows for each player ID. That's close, but not very correct. And what I need to get is a tables like this:
FOOTBALL
ID name surname position
1 John Arbuckle PHP array(goalie,defense)
HANDBALL
ID name surname position
1 John Arbuckle PHP array(pivot)
EDIT:
So what I was looking for was GROUP_CONCAT().
Thanks guys!
select p.id, p.name, p.surname, group_concat(po.name)
from players p
inner join player_position pp on pp.player_id = p.id
inner join positions po on po.id = pp.position_id
inner join sports s on s.id = po.sport_id
where s.sport = 'football'
group by p.id
See this SQLFiddle example
For football:
SELECT players.name, players.surname, positions.name
FROM players, sports, positions, player_position
WHERE sports.id = 1 AND positions.sport_id = sports.id AND payer_position.position_id = positions.id AND players.id = player_position.player_id;
For Handball:
SELECT players.name, players.surname, positions.name
FROM players, sports, positions, player_position
WHERE sports.id = 2 AND positions.sport_id = sports.id AND payer_position.position_id = positions.id AND players.id = player_position.player_id;
I'm not sure if this is foolproof (not tested) but…
SELECT pp.`player_id` AS 'ID',s.`id` AS 'sport_id',s.`sport`,ps.`name` AS 'position',pl.`name`,pl.`surname` FROM `sports` s
INNER JOIN `positions` ps ON s.`id`=ps.`sport_id`
INNER JOIN `player_position` pp ON pp.`position_id`=ps.`id`
INNER JOIN `players` pl ON pl.`id`=pp.`player_id`
ORDER BY s.`id`,pp.`player_id`,ps.`id`
Here's a more detailed version:
SELECT
s.`id` AS 'sport_id',s.`sport`,
ps.`id` AS 'position_id',ps.`name` AS 'position',
pl.`id` AS 'player_id',pl.`name`,pl.`surname`
FROM `sports` s
INNER JOIN `positions` ps ON s.`id`=ps.`sport_id`
INNER JOIN `player_position` pp ON pp.`position_id`=ps.`id`
INNER JOIN `players` pl ON pl.`id`=pp.`player_id`
ORDER BY s.`id`,pp.`player_id`,ps.`id`
my situation:
Table "_customers"
----------------------------------------------
customer_id | name | active
----------------------------------------------
1 'customer I' 1
----------------------------------------------
2 'customer II' 0
----------------------------------------------
Table "_projects"
----------------------------------------------
project_id | project_name | customer_id
----------------------------------------------
1 'project I' 1
----------------------------------------------
2 'project II' 2
----------------------------------------------
many-to-many Table "_project_sections"
----------------------------------------------
section_id | project_id
----------------------------------------------
2 1
----------------------------------------------
3 1
----------------------------------------------
4 1
----------------------------------------------
1 2
----------------------------------------------
In my Case, i need now all Customers, which are 'active'. Also they must be from a specific Section. So, as a example, i want all active customers from the Section "1". I also figured it out to get the right data, but - and thats the weird thing - only if need sections with the id > 1. If i try to get all active customers in section 1, the Query still show me all Projects with Section_id 2,3,4...
Query
SELECT c.customer_id, c.name, ps.section_id
FROM _customers c
INNER JOIN _projects p
ON p.customer_id = c.customer_id
INNER JOIN _project_sections ps
ON ps.project_id = p.project_id
WHERE c.active = 1 AND ps.section_id = 1
GROUP BY c.name
ORDER BY LOWER(c.name)
And the wrong result:
Array
(
[customer_id] => 1
[name] => customer I
[section_id] => 1
)
Maybe someone can help me, because i dont't get it. Thank you so much!
You don't need a GROUP BY clause to write this query.
First, let's look at active customers and their projects.
-- All active customers
SELECT c.customer_id, c.name
FROM customers c
WHERE c.active = 1;
customer_id name
--
1 customer I
-- All active customers and their projects
SELECT c.customer_id, c.name, p.project_id, p.project_name
FROM customers c
INNER JOIN projects p ON (p.customer_id = c.customer_id)
WHERE c.active = 1;
customer_id name project_id project_name
--
1 customer I 1 project I
1 customer I 2 project II
Just one active customer, who has two projects.
Now let's look at section 1 projects.
-- All projects from section 1
SELECT ps.project_id, ps.section_id
FROM project_sections ps
WHERE ps.section_id = 1;
project_id section_id
--
2 1
-- All projects from section 1, including project name
SELECT ps.project_id, p.project_name, ps.section_id
FROM project_sections ps
INNER JOIN projects p ON (p.project_id = ps.project_id)
WHERE ps.section_id = 1;
project_id project_name section_id
--
2 project II 1
Just one section 1 project. Now put the pieces together.
-- All active customers and their projects from section 1
SELECT c.customer_id, c.name, p.project_id, p.project_name, ps.section_id
FROM customers c
INNER JOIN projects p ON (p.customer_id = c.customer_id)
INNER JOIN project_sections ps ON (p.project_id = ps.project_id)
WHERE c.active = 1 AND ps.section_id = 1;
customer_id name project_id project_name section_id
--
1 customer I 2 project II 1
That's what I'd expect.
Later . . .
I see that you've changed the data. If I make the same changes here, then my last query above returns no rows. I think that's what you said you expected. (And, with your changes to the data, that's what I'd expect, too.)
From your description, the query should be:
SELECT c.customer_id, c.name, ps.section_id
FROM _customers c
INNER JOIN _projects p
ON p.customer_id = c.customer_id
INNER JOIN _project_sections ps
ON ps.section_id = p.project_ID
WHERE c.active = 1 AND ps.section_id = 1
GROUP BY c.name
ORDER BY LOWER(c.name)
The constraint for the specific section goes in the WHERE clause and not in the JOIN.