SQL: SELECT where 2 columns from different tables are the same - mysql

I need to work with a database that contains info about (former) Presidents. I need to check if there a presidents that have the same hobbies AND are married in the same year.
So a president can have multiple hobbies in pres_hob table. And the marriage year is in the pres_mar table, in the mar_year column.
I've tried to INNER JOIN the tables in SQLite where the hobby and mar_year are equal, except for the pres_name. This way the JOIN doesnt work ofcourse, which makes sense. Im kinda new to this..
Any help is appreciated

Here's one option with multiple joins:
select p1.pres_name, p2.pres_name, ph.hobby
from pres_mar p1
join pres_mar p2 on p1.pres_name != p2.pres_name and p1.mar_year = p2.mar_year
join pres_hob ph on p1.pres_name = ph.pres_name
join pres_hob ph2 on p2.pres_name = ph2.pres_name and ph.hobby = ph2.hobby
And depending on your expected results, another option using exists:
select pm.pres_name, ph.hobby
from pres_mar pm
join pres_hob ph on pm.pres_name = ph.pres_name
where exists (
select 1
from pres_mar pm2
join pres_hob ph2 on pm2.pres_name = ph2.pres_name
where pm.pres_name != pm2.pres_name and
ph.hobby = ph2.hobby
)

That sounds like a terrible database schema, I'm assuming its for learning purposes, anyway, you could do something like
SELECT
h.name,
h.hobby,
m.year
FROM
pres_hob h,
pres_mar m
WHERE
h.hobby = 'tennis'
AND
m.year = 2016
This would return 1 record for every president with a marriage year of 2016 , and a hobby of Tennis.

Related

Select cars that are only driven by 19-year-olds

I want to get only the cars that are only driven by 19 year old persons
If I have these tables:
The result of the query would be only the Cars with the id = 1 and id = 4
I've tried
select d.IdCar
from Persons p, Drive d
where d.IdPerson = p.IdPerson and p.Age = 19
But it gave the cars that are driven by at least one person aged 19yo.
Try this:
SELECT c.IdCar, c.Brand
FROM Car c
INNER JOIN Drive d
USING (IdCar)
INNER JOIN Persons p
USING (IdPerson)
GROUP BY d.IdCar
HAVING MIN(p.Age) = 19
AND MAX(p.Age) = 19
You can use join to get the result. But suppose your database will have to grow rapidly then I will suggest you create VIEW.
You can create a sample view by using the below command.
CREATE VIEW drive_view as (
select d.IdCar, c.Brand, d.IdPerson, p.Name, p.Age from drive d
left join Persons p on d.IdPerson = p.IdPerson
left join CARS.c on d.IdCar = c.IdCar
order by p.IdPerson ASC
);
Then you have just use SELECT the query from your program. e.g.
select * from drive_view where Age = 19;
I hope this helps. If you are at the beginning stage, I will recommend you to get some knowledge about Joins, View, Trigger, etc. It will help you a lot.
So I found this solution, and it worked for me:
So we first create a view of the dirvers with age != 19
CREATE VIEW drive_view as (
select d.IdPerson from drive d
left join Persons p on d.IdPerson = p.IdPerson
left join CARS.c on d.IdCar = c.IdCar
Where P.age <> 19
);
Then we do this query:
SELECT D.IdCar FROM Drive d JOIN Person P ON d.IdPerson = p.IdPerson WHERE p.age = 19
AND P.IdPerson NOT IN (SELECT * FROM drive-view)
So it's basically the inverse idea of #Yash: Select cars that are only driven by 19-year-olds
Wich gave all the cars that are driven by at least a 19 yo person.

SQL query for matching multiple values in the same column

I have a table in MySQL as follows.
Id Designation Years Employee
1 Soft.Egr 2000-2005 A
2 Soft.Egr 2000-2005 B
3 Soft.Egr 2000-2005 C
4 Sr.Soft.Egr 2005-2010 A
5 Sr.Soft.Egr 2005-2010 B
6 Pro.Mgr 2010-2012 A
I need to get the Employees who worked as Soft.Egr and Sr.Soft.Egr and Pro.Mgr. It is not possible to use IN or Multiple ANDs in the query. How to do this??
One way:
select Employee
from job_history
where Designation in ('Soft.Egr','Sr.Soft.Egr','Pro.Mgr')
group by Employee
having count(distinct Designation) = 3
What you might actually be looking for is relational division, even if your exercise requirements forbid using AND (for whatever reason?). This is tricky, but possible to express correctly in SQL.
Relational division in prosa means: Find those employees who have a record in the employees table for all existing designations. Or in SQL:
SELECT DISTINCT E1.Employee FROM Employees E1
WHERE NOT EXISTS (
SELECT 1 FROM Employees E2
WHERE NOT EXISTS (
SELECT 1 FROM Employees E3
WHERE E3.Employee = E1.Employee
AND E3.Designation = E2.Designation
)
)
To see the above query in action, consider this SQLFiddle
A good resource explaining relational division can be found here:
http://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-sql-of-relational-division
If you need to get additional information back about each of the roles (like the dates) then joining back to your original table for each of the additional designations is a possible solution:
SELECT t.Employee, t.Designation, t.Years, t1.Designation, t1.Years, t2.Designation, t2.Years
FROM Table t
INNER JOIN t2 ON (t2.Employee = t.Employee AND t2.Designation = 'Sr.Soft.Egr')
INNER JOIN t3 ON (t3.Employee = t.Employee AND t3.Designation = 'Soft.Egr')
WHERE t.Designation = 'Pro.Mgr';
Why not the following (for postgresql)?
SELECT employee FROM Employees WHERE Designation ='Sr.Soft.Egr'
INTERSECT
SELECT employee FROM Employees WHERE Designation ='Soft.Egr'
INTERSECT
SELECT employee FROM Employees WHERE Designation ='Pro.Mgr'
Link to SQLfiddle
I know this might not optimized, but I find this much much easier to understand and modify.
Try this query:
SELECT DISTINCT t1.employee,
t1.designation
FROM tempEmployees t1, tempEmployees t2, tempEmployees t3
WHERE t1.employee = t2.employee AND
t2.employee = t3.employee AND
t3.employee = t1.employee AND
t1.designation != t2.designation AND
t2.designation != t3.designation AND
t3.designation != t1.designation

Update Table by Join and Group by

A Company has many Reviews which has Rating Column itself.
CompID Ratig
12 3
13 3
17 4
22 4
23 5
24 3
28 3,2
This is what I need to be set to each company by id. Now Rating In Company Column is NULL.
I've written something like this:
UPDATE Companies c
JOIN Reviews r on c.CompanyID = r.CompanyID
SET c.Rating = AVG(r.rating)
group by r.CompanyID
This should do what you want using a simple nested query, in this case probably simpler than a JOIN.
UPDATE Companies
SET Rating =
(SELECT AVG(Rating)
FROM Ratings
WHERE Companies.CompanyId = Ratings.CompId)
Simple SQLfiddle demo here.
EDIT: If you really want to use a JOIN/UPDATE FROM, it'd look something like this;
UPDATE c
SET c.Rating = r.Rating
FROM Companies c
JOIN (SELECT AVG(Rating) Rating, CompID FROM Ratings GROUP BY CompId) r
ON c.CompanyId = r.CompId
At least to me, somewhat more complicated to read, and afaik it only works on SQL Server, but here's the SQLfiddle for that too :)
UPDATE ComisionesxColaboradorxLineaPrescripciones
SET CANTIDAD_PRODUCTOS_CORE_CUMPLE = CANTIDAD
FROM #ComisionesxColaboradorxLineaPrescripciones ComisionesxColaboradorxLineaPrescripciones
INNER JOIN
(SELECT TAB_L.COD_COLAB AS COD_COLAB,TAB_L.TIPO_COLABORADOR AS TIPO_COLABORADOR, COUNT(TAB_P.COD_SEG) AS CANTIDAD
FROM #ComisionesxColaboradorxLineaPrescripciones TAB_L
INNER JOIN #ComisionesxColaboradorxLineaxProductoPrescripciones TAB_P
ON TAB_L.COD_COLAB=TAB_P.COD_COLAB AND
TAB_L.TIPO_COLABORADOR=TAB_P.TIPO_COLABORADOR
GROUP BY TAB_L.COD_COLAB,TAB_L.TIPO_COLABORADOR
) AGRUPADO
ON ComisionesxColaboradorxLineaPrescripciones.COD_COLAB = AGRUPADO.COD_COLAB AND
ComisionesxColaboradorxLineaPrescripciones.TIPO_COLABORADOR = AGRUPADO.TIPO_COLABORADOR

SQL: Get latest entries from history table

I have 3 tables
person (id, name)
area (id, number)
history (id, person_id, area_id, type, datetime)
In this tables I store the info which person had which area at a specific time. It is like a salesman travels in an area for a while and then he gets another area. He can also have multiple areas at a time.
history type = 'I' for CheckIn or 'O' for Checkout.
Example:
id person_id area_id type datetime
1 2 5 'O' '2011-12-01'
2 2 5 'I' '2011-12-31'
A person started traveling in area 5 at 2011-12-01 and gave it back on 2011-12-31.
Now I want to have a list of all the areas all persons have right now.
person1.name, area1.number, area2.number, area6.name
person2.name, area5.number, area9.number
....
The output could be like this too (it doesn't matter):
person1.name, area1.number
person1.name, area2.number
person1.name, area6.number
person2.name, area5.number
....
How can I do that?
This question is, indeed, quite tricky. You need a list of the entries in history where, for a given user and area, there is an 'O' record with no subsequent 'I' record. Working with just the history table, that translates to:
SELECT ho.person_id, ho.area_id, ho.type, MAX(ho.datetime)
FROM History AS ho
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
)
GROUP BY ho.person_id, ho.area_id, ho.type;
Then, since you're really only after the person's name and the area's number (though why the area number can't be the same as its ID I am not sure), you need to adapt slightly, joining with the extra two tables:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
);
The NOT EXISTS clause is a correlated sub-query; that tends to be inefficient. You might be able to recast it as a LEFT OUTER JOIN with appropriate join and filter conditions:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
LEFT OUTER JOIN History AS hi
ON hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
WHERE ho.type = 'O'
AND hi.person_id IS NULL;
All SQL unverified.
You're looking for results where each row may have a different number of columns? I think you may want to look into GROUP_CONCAT()
SELECT p.`id`, GROUP_CONCAT(a.`number`, ',') AS `areas` FROM `person` a LEFT JOIN `history` h ON h.`person_id` = p.`id` LEFT JOIN `area` a ON a.`id` = h.`area_id`
I haven't tested this query, but I have used group concat in similar ways before. Naturally, you will want to tailor this to fit your needs. Of course, group concat will return a string so it will require post processing to use the data.
EDIT I thikn your question has been edited since I began responding. My query does not really fit your request anymore...
Try this:
select *
from person p
inner join history h on h.person_id = p.id
left outer join history h2 on h2.person_id = p.id and h2.area_id = h.area_id and h2.type = 'O'
inner join areas on a.id = h.area_id
where h2.person_id is null and h.type = 'I'

MySQL - Using column value for joining in the same query

I have three tables that looks something like this:
Table joins
|ID|JOIN_NAME|
1 persons
2 companies
Table information
|ID|JOIN_ID|
1 1
2 2
Table information_extra_persons
|ID|INFORMATION_ID|NAME|
1 1 John
Table information_extra_companies
|ID|INFORMATION_ID|NAME|
1 2 IBM
How can i join together these tables in one SQL? I've tried something like:
SELECT * FROM `information`
INNER JOIN `information_extra_(SELECT `name` FROM `joins` WHERE `id` = `join_id`)`
ON `information_extra_(SELECT `name` FROM `joins` WHERE `id` = `join_id`)`.`information_id` = `information`.`id`
but I can't get it to work. Of course this isn't my actual table setup, but it's the same principle. Does anyone know how to get all the info in just one SQL?
That's actually four tables, not three. This isn't just a nitpick - it looks as though the substance of your question is "how can I use the name of the table as part of the join criteria?" (ie. how can the information_extra_ tables be treated as a single table?)
To which the answer is: you can't. (Outside of dynamic SQL.)
In this specific case, the following should return what I think you are looking for:
select j.join_name joined_entity,
case when j.join_name = 'persons' then p.name
else c.name
end joined_entity_name
from information i
inner join joins j on i.join_id = j.id
left join information_extra_persons p on i.id = p.information_id
left join information_extra_companies c on i.id = c.information_id
Alternatively, a less efficient (but more general) approach might be:
select j.join_name joined_entity,
v.name joined_entity_name
from information i
inner join joins j on i.join_id = j.id
inner join (select 'persons' entity, information_id, name from information_extra_persons
union all
select 'companies' entity, information_id, name from information_extra_companies) v
on i.id = v.information_id and j.join_name = v.entity