I have a table "Cars" and a table "Person". A Person drives many Cars and a Car can be driven by many People so I have another table "Person_Car" which has both id's per row.
Car(id, name)
Person(id, name)
Person_Car(car_id, person_id)
How can I get a list of all people with the cars it drives (car names concatenated), something like this:
("John", "Car 1, Car 2, Car 3")
("Kate", "Car 2, Car 4, Car 5")
Example is here: http://sqlfiddle.com/#!15/ba949/1
Test data:
Create table Car(id int, name text);
Create table Person(id int, name text);
Create table Person_Car(car_id int, person_id int);
INSERT INTO Car VALUES (1, 'Car 1'),
(2, 'Car 2'),
(3, 'Car 3'),
(4, 'Car 4'),
(5, 'Car 5');
INSERT INTO Person VALUES(1, 'John'), (2, 'Kate');
INSERT INTO Person_Car VALUES (1,1), (2,1), (3,1), (2,2), (4,2), (5,2);
Your desired code:
SELECT p.name, array_to_string(array_agg(c.name), ',') FROM Person p
INNER JOIN Person_Car pc ON p.id=pc.person_id
INNER JOIN Car c ON c.id=pc.car_id
GROUP by p.name
Output:
John Car 1,Car 2,Car 3
Kate Car 2,Car 4,Car 5
Just in case you want to avoid the GROUP BY
Option 1
WITH forienKeyTable AS
(
SELECT pc.person_id, c.name
FROM Car c
JOIN Person_Car pc ON pc.car_id = c.id
)
SELECT p.name
, array_to_string
(ARRAY(
SELECT fkt.name
FROM forienKeyTable fkt
WHERE fkt.person_id = p.id
)::text[], ','::text, 'empty'::text)
FROM Person p;
Option 2
SELECT p.name
, array_to_string
(ARRAY(
SELECT c.name
FROM Car c
JOIN Person_Car pc ON pc.car_id = c.id
WHERE pc.person_id = p.id
)::text[], ','::text, 'empty'::text)
FROM Person p;
Related
I have the following tables:
CREATE TABLE books
(
codBook INTEGER PRIMARY KEY,
title CHAR(20) NOT NULL
);
INSERT INTO books
VALUES (1, 'Book 1'), (2, 'Book 2'), (3, 'Book 3');
CREATE TABLE people
(
name CHAR(10) PRIMARY KEY,
address VARCHAR(50),
CP NUMERIC(5)
);
INSERT INTO people
VALUES ('Carl', 'C/X nº 1', '12345'), ('Louis', 'C/X nº 2', '12345'),
('Joseph', 'C/Y nº 3', '12346'), ('Anna', 'C/Z nº 4', '12347');
CREATE TABLE lends
(
codBook INTEGER REFERENCES books,
member CHAR(10) REFERENCES people,
date DATE,
PRIMARY KEY (codBook, member, date)
);
INSERT INTO lends
VALUES (1, 'Joseph', CURRENT_DATE - 10),
(1, 'Carl', CURRENT_DATE - 9),
(1, 'Louis', CURRENT_DATE - 8),
(2, 'Joseph', CURRENT_DATE - 10);
I am trying to get all the rows with the title, address and CP where they were borrowed only if they were borrowed in CP=12345 and the rows that are not from CP 12345 to appear but without the address and the CP. As book 1 has CP 12345 and 12346, I only want it to appear with CP 12345.
My expected solution is:
"Book 1";"C/X nº 1";12345
"Book 1";"C/X nº 2";12345
"Book 2";null;null
"Book 3";null;null
I tried joining all the tables using 2 left joins:
SELECT title, address, CP
FROM books
LEFT JOIN lends USING (codBook)
LEFT JOIN people ON (name = member)
WHERE CP = 12345;
But I only get the rows with CP=12345 and if I remove WHERE CP=12345 I obtain all the rows, even the book 1 with CP 12346. I am looking for a way to solve this.
If you join LENDS and PEOPLE first as INNER JOIN and add the CP number to the ON clause you get your result
SELECT title , address, CP
FROM books
LEFT JOIN (lends
INNER JOIN people ON (name = member AND CP = 12345)) USING (codBook)
title
address
cp
Book 1
C/X nº 2
12345
Book 1
C/X nº 1
12345
Book 2
null
null
Book 3
null
null
SELECT 4
fiddle
I hope this query will solve your problem:
select books.title, sub.address, sub.CP
from books
left join (
SELECT address, CP, codbook
FROM books
LEFT JOIN lends USING (codBook)
JOIN people ON (name = member and CP = 12345)
) as sub on books.codbook = sub.codbook
Imagine dance teams (of varying size) can choose one or more outfits and each outfit can have one or more items. All members of a team must have all same outfits and all the items of each outfit, i.e., incomplete outfits are bad -- we want to find those bad outfits.
The tables below define two teams: APPLE and BANANA. Team APPLE has 3 members, team BANANA two members. The APPLEs have chosen a single outfit which has a single item, the YELLOW PANTSUIT outfit, naturally a crowd favorite. Team BANANA have two outfits: RED, and BLUE; but, BAILEY is missing the BANDANNAS item for the BLUE outfit. Awkward.
Let's get BAILEY out of trouble. Create a query to find missing items in the outfits chosen by the teams.
Thanks to #Brits for the SQL below:
Teams and team-members:
CREATE TABLE team (
id int unique,
name text
);
INSERT INTO team (id, name)
VALUES (1, 'APPLE'),
(2, 'BANANA');
CREATE TABLE team_member (
id int unique,
name text,
team_id int references team (id)
);
INSERT INTO team_member (id, name, team_id)
VALUES (1, 'ADAM', 2),
(2, 'BAILEY', 2),
(3, 'CATE', 1),
(4, 'DAVE', 1),
(5, 'ERIN', 1);
Outfits and outfit items:
CREATE TABLE outfit (
id int unique,
name text
);
INSERT INTO outfit (id, name)
VALUES (1, 'RED'),
(2, 'YELLOW'),
(3, 'BLUE');
CREATE TABLE outfit_item (
id int unique,
name text,
outfit_id int references outfit (id)
);
INSERT INTO outfit_item (id, name, outfit_id)
VALUES (1, 'SHORTS', 1),
(2, 'SHIRT', 1),
(3, 'PANTSUIT', 2),
(4, 'BANDANNA', 3),
(5, 'HAT', 3);
Team member outfit items:
CREATE TABLE member_item (
member_id int references team_member (id),
item_id int references outfit_item (id)
);
INSERT INTO member_item (member_id, item_id)
VALUES (1, 1),
(1, 2),
(1, 4),
(1, 5),
(2, 1),
(2, 2),
(2, 5),
(3, 3),
(4, 3),
(5, 3);
Team APPLE's members all have the YELLOW PANTSUIT outfit so the APPLEs are ready to rumble.
Team BANANA chose the RED and BLUE outfits; sadly, I failed to give team BANANA the YELLOW PANTSUIT outfit - they would have killed it, but whatever. Team BANANA's ADAM and BAILEY have the items for the RED outfit, but BAILEY does not have the BANDANNA for the BLUE outfit; let's not let a bandanna get in the way to team BANANA so we need a query to return just:
BANANA BAILEY BLUE BANDANNAS
To find the number of items in an outfit:
SELECT
o.name
, count(*) AS "number of items"
FROM
outfit_item i
, outfit o
WHERE
i.outfit_id = o.id
GROUP BY
i.outfit_id
, o.name
name | number of items
--------+-----------------
RED | 2
BLUE | 2
YELLOW | 1
Similarly, the number of members in a team:
SELECT
t.name
, count(*) AS "number of members"
FROM
team_member m
, team t
WHERE
m.team_id = t.id
GROUP BY
m.team_id
, t.name
name | number of members
--------+-------------------
BANANA | 2
APPLE | 3
That's all well and good, but how do we combine this information so we can bail BAILEY out of this BANANA blunder?
Taking on board the comment "If any member has an outfit item then all team members must have all items in that outfit" I believe the following will do what you are looking for (I'm using a CTE to work out what outfits are associated with each team).
with teamoutfit as (
-- If any mmber of a team has any part of an outfit then that team is linked to that outfit
select distinct te.id as team_id, oft.id as outfit_id from
team te
inner join team_member tm on tm.team_id = te.id
inner join member_item mi on mi.member_id = tm.id
inner join outfit_item oi on oi.id = mi.item_id
inner join outfit oft on oft.id = oi.outfit_id
)
select te.name as team, tm.name as member, of.name as outfit, oi.name as item from
team te
inner join team_member tm on tm.team_id = te.id
inner join teamoutfit tof on tof.team_id = tm.team_id
inner join outfit of on of.id = tof.outfit_id
inner join outfit_item oi on tof.outfit_id = oi.outfit_id
left join member_item mi on oi.id = mi.item_id and tm.id = mi.member_id
where
mi.member_id is null
Output:
| team | member | outfit | item |
|--------|--------|--------|----------|
| BANANA | BAILEY | BLUE | BANDANNA |
I setup a SQL Fiddle you can use to play with this (and perhaps clarify your question if I misunderstood what you are trying to do).
Confirmed to work on postgres, albeit not pretty and am 85% sure this can be simplified. Also, I was a little confused on how outfit and outfit_item tables are related. In the way you laid out the tables, bandanas and hats are are always blue, pantsuits are always yellow and shorts/shirts are always red when you join outfit to outfit_item (outfit.id = outfit_item.outfit_id). The test data and query has been written with that understanding.
select distinct
tm.name as member_name,
t.name as team_name,
o.name as outfit_color,
oi.name as outfit_item
from team_member tm
join team t
on t.id = tm.team_id
join member_items mi
on mi.member_id = tm.id
join(
with outfit_item_count as
(SELECT outfit_id, count(*) AS items
FROM outfit_items
GROUP BY 1)
select mi.member_id, oi.outfit_id, oic.items as items_needed, count(mi.item_id) as member_item_count
from member_items mi
join outfit_items oi on oi.id = mi.item_id
join outfit_item_count oic on oic.outfit_id = oi.outfit_id
group by 1,2,3)z
on z.member_id = tm.id
join outfit_items oi
on oi.outfit_id = z.outfit_id
join outfit o
on o.id = oi.outfit_id
where z.items_needed > z.member_item_count
and oi.id not in (select item_id from member_items)
So, I basically have two tables Dogs and Litter
Table Dogs:
ID(PK) Litter_ID Name
---------------------------------
1 null Fido
2 null Freda
3 11 Pedro
4 11 John
5 22 Maria
6 33 Billy
7
Table Litter:
Litter_ID (PK)------Father_id---------- Mother_id
11---------------------1-----------------2
33---------------------4-----------------5
How do I find all father mothers and grandparents of a specific dog?
This gives me only the parents, but how about the grandparents if there are some?
SELECT Dogs.id, Name, Father_id, Mother_id
FROM Dogs, Litter
WHERE Dogs.litter_id = litter.litter_id AND dogs.id = 6;
So if I search for Billy, I should get Maria and John and then John's parents Fido and Freda.
I'd appreciate some help
Presuming you are using SQL Server, the following should work.
create table Dogs (ID int, Litter_ID int, Name varchar(10));
create table Litter (Litter_ID int, Father_ID int, Mother_ID int);
insert into Dogs values (1, null, 'Fido')
insert into Dogs values (2, null, 'Freda')
insert into Dogs values (3, 11, 'Pedro')
insert into Dogs values (4, 11, 'John')
insert into Dogs values (5, 22, 'Maria')
insert into Dogs values (6, 33, 'Billy')
insert into Litter values (11, 1, 2)
insert into Litter values (33, 4, 5)
SELECT
Parents.Name AS Parent,
GrandParents.Name AS GrandParent
FROM Dogs
inner join Litter on Dogs.Litter_ID = Litter.Litter_ID
inner join Dogs Parents on Parents.ID = Litter.Father_ID or Parents.ID = Litter.Mother_ID
left join Litter ParentsLitter on ParentsLitter.Litter_ID = Parents.Litter_ID
left join Dogs GrandParents on GrandParents.ID = ParentsLitter.Father_ID or GrandParents.ID = ParentsLitter.Mother_ID
WHERE dogs.id = 6;
I have 3 tables (People, ProjectGroupAssoc and Projects), and I'm looking to show all people who do not have a specific project_Id. As in, I would like to have the query return the person's name but NULL in the project_id, and project_name columns.
EDITED
Let's say Tim should be assigned a 'Database' project; however, he currently doesn't have a database assignment in the ProjectGroupAssoc table. How would one query Tim, and any other "people" who don't have 'Database' projects, without returning multiple row for each person (i.e. returning just those people with NULL for database projects, and not the other projects they have been assigned?
This is to ensure certain "people" have been assigned their respective projects for tracking and auditing. Here's my sample database/tables/relationships.
create table People (
person_Id int,
name varchar(255)
);
INSERT INTO People (person_Id, name)
VALUES (0, 'John'), (1, 'Paul'), (2, 'Tim');
create table ProjectGroupAssoc (
person_Id int,
groupId int
);
INSERT INTO ProjectGroupAssoc (person_Id, groupId)
VALUES (0, 255), (1, 1700), (2, 35), (0, 17), (0, 333), (1, 255)
CREATE TABLE Projects (
proj_Id int,
p_name varchar(255)
);
INSERT INTO Projects(proj_Id, p_name)
VALUES (255, 'Database'), (1700, 'Development'), (333, 'Training'),
(35, 'security'), (17, 'analytics')
select p.person_Id, p.name, pga.groupId, pro.p_name
from People p
left join ProjectGroupAssoc pga on p.person_Id = pga.person_Id
left join Projects pro on pga.groupId = pro.proj_Id
where pga.groupId = 255;
I guess you are looking for something like this.
select p.Id, p.name, pga.groupId, pro.p_name
from People p
left join ProjectGroupAssoc pga on p.Id = pga.Id AND pga.groupId = 255
left join Projects pro on pga.groupId = pro.Id
If you want show people who do not have a project id
SELECT p.person_Id
FROM People p
WHERE NOT EXISTS (SELECT 1 FROM ProjectGroupAssoc pga WHERE p.person_Id = pga.person_Id AND pga.groupId = 255)
I have the following relation:
CompanyInfo(company, role, employee)
What I'm trying to do is to find the shortest "path" between two employees.
Example
I need to find the distance between Joe and Peter.
Joe is the CEO of Company A, and a person named Alex is a board member.
Alex is the CEO of Company B, and Peter is a vice president at Company B. Then, the distance between Joe and Peter will be 2. If Joe and Peter had roles in the same company, it would be 1.
I need to solve this using recursive SQL. So far I've come up with the base case and the final select string, but I can't for the life of me figure out the recursive part.
WITH RECURSIVE shortest_path(c1,p1,c2,p2, path) AS (
-- Basecase --
SELECT c1.company, c1.person, c2.company, c2.person, array[c1.person, c2.person]
FROM CompanyInfo c1
INNER JOIN CompanyInfo c2 ON c1.company = c2.company
WHERE c1.person = 'Joe'
AND c1.person <> c2.person
UNION ALL
-- Recursive --
-- This is where I'm stuck.
)
SELECT p1, p2, array_length(path,1) -1 as distance
FROM shortest_path
WHERE p2 = 'Peter'
ORDER BY distance
LIMIT 1;
Sample Data
CREATE TABLE CompanyInfo (
company text,
role text,
employee text,
primary key (company, role, employee)
);
insert into CompanyInfo values('Company A', 'CEO', 'Joe');
insert into CompanyInfo values('Company A', 'Board member', 'Alex');
insert into CompanyInfo values('Company B', 'CEO', 'Alex');
insert into CompanyInfo values('Company B', 'Board member', 'Peter');
Expected Output
person 1 | person 2 | distance
Joe Peter 2
Try this. Keep running till new employee can be added to the path.
CREATE TABLE CompanyInfo (
company text,
role text,
employee text,
primary key (company, role, employee)
);
insert into CompanyInfo values('Company A', 'CEO', 'Joe');
insert into CompanyInfo values('Company A', 'Board member', 'Alex');
insert into CompanyInfo values('Company B', 'CEO', 'Alex');
insert into CompanyInfo values('Company B', 'Board member', 'Peter');
WITH RECURSIVE shortest_path(c1,p1,c2,p2, path) AS (
-- Basecase --
SELECT c1.company, c1.employee, c2.company, c2.employee, array[c1.employee, c2.employee]
FROM CompanyInfo c1
JOIN CompanyInfo c2 ON c1.company = c2.company
AND c1.employee = 'Joe'
AND c1.employee <> c2.employee
UNION ALL
-- Recursive --
SELECT c1, p1, c3.company, c3.employee, path || c3.employee
FROM shortest_path c1
JOIN CompanyInfo c2 ON c1.p2 = c2.employee
JOIN CompanyInfo c3 ON c3.company = c2.company
AND NOT c3.employee = ANY (c1.path)
)
SELECT *, array_length(path,1) -1 as distance
FROM shortest_path
WHERE p2 = 'Peter'
ORDER BY distance
LIMIT 1;