Inner Join returned values - mysql

I have a query such as,
select name
from employee
inner join task on employee.id = task.employee_id
order by name asc
Tables look like:
employee
id name
1 Emily
2 Sam
3 AI
4 Joe
5 Daniel
6 John
task
task_id employee_id
A123 1
D456 3
A122 1
I believed the original query above would result in:
Al
Emily
But it is actually:
Al
Emily
Emily
I thought it would just return Emily once since inner join returns both values in both tables, but doesnt Emily appear only once in the employee table? I dont understand why it returns Emily twice even though it is listed twice in the task table?
Thanks

Emily has two tasks, hence her name record gets duplicated in the join, once for each match. I might use exists logic to get the result you want here:
SELECT e.name
FROM employee e
WHERE EXISTS (SELECT 1 FROM task t WHERE t.employee_id = e.id);
Read in plain English, the above query says to return any employee name (once) for which we can find at least one task in the task table.

Related

mysql join query where multiple rows have same key value

I have a table, in a third party database, that has two tables like these:
HISTORY
========
ID | ORDERED
1 PEAS
1 CARROTS
1 SPINACH
2 CARROTS
3 PEAS
3 CARROTS
PEOPLE
=====
ID | NAME
1 Jamal
2 Sharon
3 Mark
I am trying to create a MYSQL query that will return all the PEOPLE who ORDERED both PEAS and CARROTS. The results would be:
Jamal, Mark
When I try this with the OR operator, I get all three people:
SELECT a.ID from people a
INNER JOIN history b on a.ID=b.ID
WHERE b.ordered='PEAS' OR b.ordered='CARROTS'
When I try this with the AND operator, I get no people.
SELECT a.ID from people a
INNER JOIN history b on a.ID=b.ID
WHERE b.ordered='PEAS' AND b.ordered='CARROTS'
How can I write a query to get the names of the people who ordered peas and carrots given the table structure I have to work with?
JOIN twice, once for each condition:
SELECT a.ID
FROM people a
JOIN history b on a.ID=b.ID AND b.ordered='PEAS'
JOIN history c on a.ID=c.ID AND c.ordered='CARROTS'
If history can contain duplicates, or to be defensive, add DISTINCT:
SELECT DISTINCT a.ID
FROM ...
Select all people from people and for each of them the history details, but only the people who ordered PEAS or CARROTS:
When you say "all" people, then you always begin your sql with the people table. When you say "who have", then you add the LEFT JOIN, e.g to the LEFT table (people) you JOIN the wanted details from the right table (history). And when you say "but just the ones having the following details", then you apply a filter, e.g. you use WHERE clause.
SELECT
peo.ID, -- OPTIONAL
peo.NAME,
his.ID, -- OPTIONAL
his.ORDERED
FROM people AS peo
LEFT JOIN history AS his ON his.ID = peo.ID
WHERE
his.ORDERED = "PEAS" OR
his.ORDERED = "CARROTS"
;
Note: "--" means commentar.
EDIT 1:
Important, a principle which you always should apply: Rename the history.ID column to history.PEOPLE_ID and add a new column history.ID as PRIMARY KEY column, in order to uniquely identify each historyrecord.
HISTORY
-------
ID PEOPLE_ID ORDERED
1 1 PEAS
2 1 CARROTS
3 1 SPINACH
4 2 CARROTS
5 3 PEAS
6 3 CARROTS
PEOPLE table remains the same.
EDIT 2:
NO no, it should be an AND in the WHERE clause. My fault. I corrected it.
EDIT 3:
NO no, it should be an OR in the WHERE clause. My fault. I corrected it. AGAIN :-)))

MYSQL Left Join filtering when child records do not always exist

MYSQL
parent table
customer
C_Id
First
Last
child table
payments
P_Id
Paid (logical)
C_Id
All customer records do not have a child record in the payment table.
Want to retrieve all customer records except those that have a child record in the payment table where pay = True . The data in the logical field paid may be True, False, or NULL
So the result set of records will exclude any customers that were already paid.
Left Join gives me this:
1 Harry Houdini
2 Johnny Bench
3 Clark Gable Null
4 John Galt False
5 Nick Cage True
6 Casey Cason
The desired result is
1 Harry Houdini
2 Johnny Bench
3 Clark Gable Null
4 John Galt False
6 Casey Cason
This record does not meet the condition so it is excluded from the query
5 Nick Cage True
try this
select * from customer c
join payment p
on c.c_id = p.c_id where pay = 'false' or pay is NULL;
you can't got those output using left join because using left join you would got whole table.you can do this using inner join

MySQL, How to select one row from first table and two from the second one

I need to SELECT one row from first table and two rows from second one that correspond to the result from the first one. I can explain it more clearly using example.
As shown below I have two tables. Table "Games" have information about games between teams (e.g. game nr 1 is played by team NR 10 and team NR 11). In second table I simply have teams' names. The question is how to using one Select get information about game together with teams names from second table?
e.g. I want to get information who plays game NR 3 together with teams' names. Result should be:
id = 3, team_nr1 = 14, team_nr2 = 15, team1_name = eee, team2_name = fff
Is it even possible?
Table: Games
________________________
id team_nr1 team_nr2
1 10 11
2 12 13
3 14 15
Table: Teams
________________________
team_id team_name
10 aaa
11 bbb
12 ccc
13 ddd
14 eee
15 fff
Thanks for any advice.
I think you want to use joins. It could look like this:
SELECT G.id, G.team_nr1, G.team_nr2,
T1.team_name AS team1_name, T2.team_name AS team2_name
FROM Games G
LEFT JOIN Teams T1 ON (G.team_nr1 = T1.team_id)
LEFT JOIN Teams T2 ON (G.team_nr2 = T2.team_id)
WHERE G.id = 3
There are also other types of joins. LEFT JOIN means that all games are listed, even if there are no matching teams in T1 or T2.
You could try:-
SELECT ID,
TEAM_NR1,
TEAN_NR2,
(SELECT TEAM_NAME
FROM TEAMS
WHERE TEAMS.TEAM_ID = GAMES.TEAM_NR1) AS TEAM1_NAME,
(SELECT TEAM_NAME
FROM TEAMS
WHERE TEAMS.TEAM_ID = GAMES.TEAM_NR2) AS TEAM2_NAME
FROM GAMES
WHERE GAMES.ID = 3;
The subqueries pick out the required team names and name the "column" in which they appear.
If you want to have only 1 row with all your values, the only way I can thing of is using the GROUP_CONCAT function. I would not recommend the use on inline queries oe subqueries because in case of no record found it will produce a mysql error.

Mysql (conditional?) query from two tables

Not sure if I have phrased the title properly, but here it goes. I have these two tables:
table:staff
id Name groupId Status
1 John Smith 1 1
2 John Doe 1 1
3 Jane Smith 2 1
4 Jerry Smith 1 1
table:jobqueue
id job_id staff_id jobStatus
1 1 1 1
2 2 1 1
3 5 2 1
4 7 3 0
Now, what I need to do is to find the staff with the least amount of job assigned to him which I am able to do by querying the jobqueue table.
SELECT min(cstaff),tmp.staff_id FROM (SELECT t.staff_id, count(staff_id) cstaff from jobqueue t join staff s on t.staff_id=s.id join group g on s.groupId=g.id where g.id=26 GROUP BY t.id ) tmp
This works fine, but the problem is if a staff is not assigned to any job at all, this query wont get them, because it only queries the jobqueue table, where that particular staff won't have any entry. I need to modify the query to include the staff table and if a staff is not assigned any job in the jobqueue then I need to get the staff details from the staff table. Basically, I need to find staff for a group who are not assigned any job and if all staffs are assigned job then find staff with the least amount of jobs assigned. Could use some help with this. Also, tagging as Yii as I would like to know if this is doable with Yii active-records. But I am okay with a plain sql query that will work with Yii sql commands.
not sure that it is optimal query, but it works:
select d.groupId, d.name, (select count(*) from jobqueue as e where e.staff_id=d.id) as jobassigned
from staff as d
where d.id in (
select
(
select a.id
from staff as a
left outer join
jobqueue as b
on (a.id = b.staff_id)
where a.groupId = c.groupId
group by a.id
order by count(distinct job_id) asc
limit 1
) as notassigneduserid
from (
select distinct groupId from staff
) as c)
maybe need some comments:
c query is needed to get all distinct groupId - if you have separate table for this, you can replace it
notassigneduserid statement for each groupId select user with minimal job count
d query is needed to fetch actual user names, groupId for all found "unassigned users" and present it
here is the results for data from question:
Group Staff Jobs assigned
1 Jerry Smith 0
2 Jane Smith 1
with
counts as (
select s.groupId
, s.id
, (select count(*) from jobqueue where staff_id = s.id) count
from staff s
group by s.id, s.groupId),
groups as (
select groupId, min(count) mincount
from counts
group by groupId)
select c.groupId, c.id, c.count
from counts c
join groups g on c.groupId = g.groupId
where c.count = g.mincount
This SQL will give you all the staff with the minimum number of jobs in each group. It might be that more than one staff has the same minimum number of jobs. The approach is to use common table expressions to build first a list of counts, and then to retrieve the minimum count for each group. Finally I join the counts and groups tables and retrieve the staff that have the minimum count for each group.
I tested this on SQL Server, but the syntax should work for MySQL as well. To your data I added:
id Name groupId Status
5 Bubba Jones 2 1
6 Bubba Smith 1 1
and
id job_id staff_id jobStatus
5 4 5 1
Results are
group name count
1 Bubba Smith 0
1 Jerry Smith 0
2 Bubba Jones 1
2 Jane Smith 1
BTW, I would not try to do this with active record, it is far too complex.
As Ilya Bursov said this answer wasn't respond exactly what was asked. So here is a more optimized solution:
SELECT *
FROM (
SELECT s.id as id_staff, s.Name, s.groupId, count(distinct t.id) as jobsXstaff
FROM staff s
LEFT JOIN jobqueue t ON s.id=t.staff_id
GROUP BY s.id, s.groupId
ORDER BY s.groupId, jobsXstaff
) tmp
GROUP BY groupId
Old answer below.
This works but without table group which I don't create. You can simply join table groups as you did:
SELECT min(cstaff),tmp.id
FROM (
SELECT s.id, count( staff_id ) cstaff
FROM jobqueue t
RIGHT JOIN staff s ON t.staff_id = s.id
GROUP BY t.id
) tmp
As you see you need to get all values from table staff (right join) and select the id staff from it's own table (s.id instead of t.staff_id). Also you have to get tmp.id instead of staff_id now.

mysql left join duplicates

ive been searching for hours but cant find a solution. its a bit complicated so i'll break it down into a very simple example
i have two tables; people and cars
people:
name_id firstname
1 john
2 tony
3 peter
4 henry
cars:
name_id car_name
1 vw gulf
1 ferrari
2 mustang
4 toyota
as can be seen, they are linked by name_id, and john has 2 cars, tony has 1, peter has 0 and henry has 1.
i simply want to do a single mysql search for who has a (1 or more) car. so the anwser should be john, tony, henry.
the people table is the master table, and im using LEFT JOIN to add the cars. my problem arises from the duplicates. the fact that the table im joining has 2 entries for 1 id in the master.
im playing around with DISTINCT and GROUP BY but i cant seem to get it to work.
any help is much appreciated.
EDIT: adding the query:
$query = "
SELECT profiles.*, invoices.paid, COUNT(*) as num
FROM profiles
LEFT JOIN invoices ON (profiles.id=invoices.profileid)
WHERE (profiles.id LIKE '%$id%')
GROUP BY invoices.profileid
";
try this
select distinct p.name_id, firstname
from people p, cars c
where p.name_id = c.name_id
or use joins
select distinct p.name_id, firstname
from people p
inner join cars c
on p.name_id = c.name_id
If you only want to show people that have a car, then you should use a RIGHT JOIN. This will stop any results from the left table (people) to be returned if they didn't have a match in the cars table.
Group by the persons name to remove duplicates.
SELECT firstname
FROM people P
RIGHT JOIN cars C ON C.name_id = P.name_id
GROUP BY firstname
SELECT DISTINCT firstname
FROM people
JOIN cars ON cars.name_id = people.name_id;
If this doesn't work you might have to show us the full problem.
The way to propose it there's no need for a left join since you need at least a car per person. Left join is implicitely an OUTER join and is intended to return the results with 0 corresponding records in the joinned table.