mysql join query where multiple rows have same key value - mysql

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 :-)))

Related

Inner Join returned values

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.

MYSQL subquery with a join

I am trying to retrieve records from 4 tables and want to use a sub query within a left Join. So basically, i have
table a is the table for openings
o,id, o.namesdesc
table b is the table of all the projects
b.id, b.titledesc,b,status,b.edate
table a_b is the table for all the opening that have changed to projects acts like a junction table for one to many
o.id, b.id
table br is the relationship table with other projects
b.id b_rel_code (this is also a b.id as in related to which other projects)
I want to get all the openings,check whether they have project codes from the table a_b table get the projects codes relationship with other project codes check the status of the projects and get all the end_dates of the projects
I would really appreciate your help on this. I have been banging my head from ages. I think i am just not seeing it through
SELECT
o.id,
o.namesdesc,
GROUP_CONCAT(ab.pcode) as junctionpcode,
GROUP_CONCAT(br.scode) as proj_link_code,
GROUP_CONCAT(b.status)
FROM table_ab as ab
Left JOIN tablea as o on ab.oid = o.id
Left Join tableb as b on ab.pcode= b.id
LEFT Join tablebr as br on br.b_rel_code=b.id
where b.status='E'
GROUP BY o.id order by junctionpcode
Many Thanks,
Ok, Probably i have not explained well. May be sample data should help this
Tablea lists description of openings
id,namedesc
1,test desc1
2,test desc2
Tableb list of all the projects
pcode,pname,status,edate
001,test pr1,E,30-10-2017
002,test pr2,E,30-05-2017
003,test pr3,A,30-10-2018
Table br is the relationship table for the projects that are related to other projects
brid pcode rel_pcode
1 001 003
2 003 001
Table a_b which is the junction table between tablea and tableb
abid pcode id
1 001 1
2 002 2
3 003 3
So what i want to achieve is get all the openings(from tablea) that has project codes associated with it(from tablea_b) and get the status and enddates of the projects for only with status E(from table b). But it has needs to consider if that project code has the link to the project code(from table br) which has status and edate.
The output that i am expecting is
a.id,concat(pcode+relcodeany),concat(pstatus,relstatus),concat(pedate,relcodeedate)
1 ,(001,003),(E,A),(30-10-2017,30-10-2018)
I hope i have provided enough info.
Sorry for the long post
What was the problem with the query?
You did not add any sample data or expected result, but couple of notes from the query:
table tablebr is joined through table tableb (br.b_rel_code=b.id), if tableb does not contain any rows for ab.pcode nor you will get any rows from tablebr
The b.status='E' where clause turns the left join for tableb into ordinary join (only rows that match will be included). If you want the left join to be effective move the condition after the ON (on ab.pcode= b.id and b.status='E')
Please provide sample data and expected result.

SQL search in relations

I have a main table with two relations.
Data structure and example:
A/Employee
id fields
1 Mike Miller
2 Lisa Miller
B/Skill
aid name
1 SQL
1 PHP
C/Language
aid name
1 German
I need a query which shows results from the main table and searches for a keyword in the relation tables.
Search for Miller -> Mike Miller, Lisa Miller
Search for SQL -> Mike Miller
Search for German -> Mike Miller
There are 10.000 rows in the main table and 100.000 relations.
I tried it with JOIN but the query is really slow.
Also the same row from the main table is displayed a view several times when there are more than one relations for this row:
Search for Miller
Returns: Mike Miller, Mike Miller
(Mike Miller displayed more than one time)
SELECT fields
FROM A
JOIN B ON id = B.aid JOIN C ON id = C.aid
WHERE fields LIKE '%"+$search+"%' OR B.name LIKE '%"+$search+"%' OR C.name LIKE '%"+$search+"%'"
I tried to fix the second problem with DISTINCT but now rows without relations are not displayed.
I want to display every row from the main table exactly one time. Which query do I need?
The problem with your first query, as you mention yourself, is that you get several duplicate rows returned. Not strange, since I guess the relation between table A and table B & C is one-to-many.
In your next attempt you added DISTINCT, and that will indeed get rid off the duplicates, but the regular join (or inner join) will only return matches where data can be joined, i.e. where data exists in both joined tables.
Introducing LEFT JOIN:
SELECT DISTINCT fields
FROM A
LEFT JOIN B ON id = B.aid
LEFT JOIN C ON id = C.aid
WHERE fields LIKE '%"+$search+"%' OR B.name LIKE '%"+$search+"%' OR C.name LIKE '%"+$search+"%'"
This will always search all data from table A, and those from table B & C where joins can be made. The DISTINCT will make sure that only unique rows are returned. You could also use GROUP BY for the same result, but that's usually used for aggregate methods.
Use LEFT JOIN.
Example:
SELECT distinct e.empname FROM Employee e
LEFT JOIN skill s ON s.aid = e.id
LEFT JOIN lang l ON l.aid = e.id
WHERE e.empname LIKE '%Miller%' OR s.name LIKE '%Miller%' OR l.name LIKE '%Miller%'
Proof SQL Fiddle

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.

mysql simple subquery problem

table user
____________________________________________
id name nickname info_id
1 john apple 11
2 paul banana 12
3 pauline melon 13
table info
_____________________________________________
id job location
11 model usa
12 engineer russia
13 seller brazil
result I want
______________________________________________
1 john apple model usa
my query
left join:
select * from user a left join info b on b.id = a.info_id where a.id=1
subquery:
select a.*, b.* from (user a, info b) where b.id = a.info_id
which is better?
SELECT a.`name`, a.`nickname`, b.`job`, b.`location`
FROM `user` AS a
LEFT JOIN `info` AS b
ON ( a.`info_id` = b.`id` )
That should be pretty efficient. Try using MySQL EXPLAIN if you are concerned (also make sure there are indexes on the ID fields):
http://dev.mysql.com/doc/refman/5.1/en/using-explain.html
UPDATE
After seeing that you are not having performance problems just yet, I would not worry about it. "Don't fix what ain't broken". If you find that it is slowing down in the future, or it is bottle-necking on that function, then worry about it.
The query I gave should be pretty efficient.