SQL Simple SELECT Query - sql

create table Person(
SSN INT,
Name VARCHAR(20),
primary key(SSN)
);
create table Car(
PlateNr INT,
Model VARCHAR(20),
primary key(PlateNr)
);
create table CarOwner(
SSN INT,
PlateNr INT,
primary key(SSN, PlateNR)
foreign key(SSN) references Person (SSN),
foreign key(PlateNr) references Car (PlateNr)
);
Insert into Person(SSN, Name) VALUES ('123456789','Max');
Insert into Person(SSN, Name) VALUES ('123456787','John');
Insert into Person(SSN, Name) VALUES ('123456788','Tom');
Insert into Car(PlateNr, Model) VALUES ('123ABC','Volvo');
Insert into Car(PlateNr, Model) VALUES ('321CBA','Toyota');
Insert into Car(PlateNr, Model) VALUES ('333AAA','Honda');
Insert into CarOwner(SSN, PlateNr) VALUES ('123456789','123ABC');
Insert into CarOwner(SSN, PlateNr) VALUES ('123456787','333AAA');
The problem I'm having is the SELECTE query I wanna make. I wan't to be able to SELECT everything from the Person and wan't the include the PlateNr of the car he's the owner of, an example:
PERSON
---------------------------------
SSN NAME Car
123456789 Max 123ABC
123456787 John 3338AAA
123456788 Tom
----------------------------------
So, I want to be able to show everything from the Person table and display the content of CarOwner aswell if the person is in fact a CarOwner. What I have so far is: "SELECT * from Person, CarOwner WHERE Person.SSN = CarOwner.SSN;". But this obviously results in only showing the person(s) that are CarOwners.
Hope I explained me well enough, Thanks.

Try this:
SELECT p.*, c.*
FROM Person p
LEFT OUTER JOIN CarOwner co
ON p.SSN = co.SSN
LEFT OUTER JOIN Car c
ON co.PlateNr = c.PlateNr
Show SQLFiddle
P.S. I've changed the type of your primary key PlateNr (in varchar and not in int)

select ssn, name, car
from Person p
LEFT OUTER JOIN CarOwner co
ON p.SSN = co.SSN
LEFT OUTER JOIN Car c
ON co.PlateNr = c.PlateNr

Related

SQL insert multiple rows depending on number of rows returned from subquery

I have 3 SQL tables Companies, Materials and Suppliers as follows.
Tables
I need to insert values into Suppliers from a list which contains Company Name and Material Name as headers. However, I have multiple companies with the same name in the database and i need to add a new value into suppliers for each one of those companies.
For e.g. my list containes values ['Wickes','Bricks'] . I have this sql below to add a new entry into the suppliers table but since i have multple companies called 'Wickes' I'll get an error as the subquery will return more than 1 value.
INSERT INTO Suppliers(Id,CompanyId,MaterialId) VALUES (NEWID(), (SELECT Id FROM Companies WHERE Name = 'Wickes'),(SELECT Id FROM Materials WHERE Name = 'Bricks'))
Whats the best solution to get the Id of all the companies there are called 'Wickes' and then add vales into the suppliers table with that Id and the relevant material Id of 'Bricks'.
You can use INSERT () SELECT.. rather than INSERT () VALUES(), e.g
INSERT INTO Suppliers (Id, CompanyId, MaterialId)
SELECT NEWID(), c.Id, m.Id
FROM Companies AS c
CROSS JOIN Materials AS m
WHERE c.Name = 'Wickes'
AND m.Name = 'Bricks';
This will ensure that if you have multiple companies/materials with the same name, all permutations are inserted. Example on db<>fiddle
Although based on your image Suppliers.Id is an integer, so I think NEWID() is not doing what you think it is here, you probably just want:
INSERT INTO Suppliers (CompanyId, MaterialId)
SELECT c.Id, m.Id
FROM Companies AS c
CROSS JOIN Materials AS m
WHERE c.Name = 'Wickes'
AND m.Name = 'Bricks';
And let IDENTITY take care of the Id column in Suppliers.
As a further aside, I've also just noted that MaterialId is VARCHAR in your Suppliers table, that looks like an error if it is supposed to reference the integer Id column in Materials.
INSERT INTO Suppliers(Id,CompanyId,MaterialId) VALUES (NEWID(), (SELECT distict Id FROM Companies WHERE Name = 'Wickes'),(SELECT distict Id FROM Materials WHERE Name = 'Bricks'));
If I understand rightly Companies are the suppliers and the Suppliers table is the one that says where you can buy each material from.
Why do you have duplicates? Do you have an account for different branches of Wickes for example? If they are really duplicates and you don't care which one you use a function like MIN() will do the job of ensuring that only one value is returned. If you have duplicates it would be a good idea to find a way of disactivating all except one. This will make is simpler for you everytime you want to deal with the supplier: minimum orders, chasing overdue orders, payments etc.
Also Companies.ID and Materials.ID should be foreign keys of the Suppliers table. It is also a good idea for the ID column to be auto-incrementing, which makes it easier to add new products as you do not need to specify the ID column.
If you cannot or do not want to modify the id column to auto-incrementing IDENTITY you can continue to use NEWID().
create table Companies(
id INT PRIMARY KEY NOT NULL IDENTITY,
name VARCHAR(25));
create table Materials(
id INT PRIMARY KEY NOT NULL IDENTITY,
name VARCHAR(25));
create table Suppliers(
id INT PRIMARY KEY NOT NULL IDENTITY,
CompanyId INT FOREIGN KEY REFERENCES Companies(id),
MaterialId INT FOREIGN KEY REFERENCES Materials(id)
);
INSERT INTO Companies (name) VALUES ('Wickes');
INSERT INTO Materials (name) VALUES ('Bricks');
INSERT INTO Suppliers ( CompanyId, MaterialId)
SELECT c.Id, M.Id
FROM Companies AS c
CROSS JOIN Materials AS m
WHERE c.Name = 'Wickes'
AND m.Name = 'Bricks';
SELECT * FROM Companies;
SELECT * FROM Materials;
SELECT * FROM Suppliers;
GO
id | name
-: | :-----
1 | Wickes
id | name
-: | :-----
1 | Bricks
id | CompanyId | MaterialId
-: | --------: | ---------:
1 | 1 | 1
db<>fiddle here
INSERT INTO SUPPLIERS
(ID, COMPANYID, MATERIALID)
VALUES (NEWID(),
(SELECT DISTINCT ID FROM COMPANIES WHERE NAME = 'Wickes'), (SELECT DISTINCT ID FROM MATERIALS WHERE NAME = 'Bricks'))

Find all the persons which does not contain the provided resources

Below are the tables
CREATE TABLE Person(
PersonID INT PRIMARY KEY,
FirstName VARCHAR(10),
LastName VARCHAR(10));
CREATE TABLE Resources(
ResourceID CHAR(3) PRIMARY KEY
);
CREATE TABLE PR (
PersonID INT,
ResourceID CHAR(3),
CONSTRAINT pkpr PRIMARY KEY (PersonID, ResourceID),
CONSTRAINT fkPersonID FOREIGN KEY (PersonID) REFERENCES Person(PersonID),
CONSTRAINT fkResourceID FOREIGN KEY (ResourceID) REFERENCES Resources(ResourceID));
INSERT INTO Person(PersonID, FirstName, LastName) VALUES (1, 'Bill', 'Smith'),(2, 'John','Jones'), (3, 'Tim', 'Jolt');
INSERT INTO Resources (ResourceID) VALUES ('ABC'),('DEF'),('HIJ');
INSERT INTO PR (PersonID, ResourceID) VALUES (1,'ABC'),(1,'DEF'),(2,'ABC'), (2,'HIJ'), (1,'HIJ'), (3, 'DEF');
How to find all the persons which does not have resources ('ABC', 'HIJ') ?
With above inserted data it should return person Tim Jolt
I am using PostgreSql.
So your main source of entity is the Person table. And you need to ignore all Persons which have the given resources.
So the SQL will be like below.
select PersonID from Person where PersonID not in ( select PersonID from PR where ResourceID in ('ABC', 'DEF'))
You can write your query in following 4 ways:
Using NOT IN: same answered by Pratik Soni
select personid from person
where personid not in ( select personid from PR where resourceid in ('ABC', 'HIJ'))
Using NOT EXIST:
select personid from person t1
where not exists (select 1 from PR where personid=t1.personid and resourceid in ('ABC', 'HIJ'))
Using <> ALL:
SELECT personid FROM person WHERE
personid <> ALL(select personid from PR where resourceid in ('ABC', 'HIJ'))
Using LEFT JOIN and IS NULL
SELECT p.personid
FROM person p
LEFT JOIN PR r ON p.personid = r.personid AND r.resourceid in ('ABC', 'HIJ')
where r.personid is null
All 4 methods have their own pros and cons. No body can predict the performance without seeing Explain Analyze result. So check the execution plan using all above queries with real data and decide accordingly what method you should adopt.
DEMO

Trying to do a three table join displaying all columns

I am trying to do a three table join to display all columns. Only two of the tables have a key in common. Below is the code for the tables I already have:
CREATE TABLE Faculty (
FacultyID int,
FirstName varchar(30),
LastName varchar(30),
EMail varchar(60),
BirthDate DATE,
Numbr_Courses int,
PRIMARY KEY (FacultyID)
);
CREATE TABLE Courses(
CourseID int,
CourseDisc varchar(4),
CourseNum varchar(4),
NumbrCred varchar(1),
FirstYrOffered int,
CourseTitle varchar(75),
PRIMARY KEY (CourseID)
);
CREATE TABLE Faculty_Courses(
InstanceID int,
FacultyID int,
CourseDisc varchar(4),
CourseNum varchar(4),
CourseTitle varchar(75),
PRIMARY KEY (InstanceID),
FOREIGN KEY (FacultyID) REFERENCES Faculty(FacultyID)
);
The two tables that have the same key is Faculty and Faculty_Course. I have attempted one peice of code that only gave me back an error. I guess I am having real trouble understanding how to do proper code for joining tables. My attempted code is below:
SELECT Faculty.*, Faculty_Courses.*
FROM Faculty INNER JOIN Courses
ON Faculty.FacultyID=Faculty_Courses.FacultyID
This gave me back the following error:
ERROR 1066 (42000): Not unique table/alias: 'Faculty'
Any help will be appreciated.
I would change your Faculty_Courses table:
CREATE TABLE Faculty_Courses(
InstanceID int,
FacultyID int,
CourseID int,
PRIMARY KEY (InstanceID),
FOREIGN KEY (FacultyID) REFERENCES Faculty(FacultyID),
FOREIGN KEY (CourseID) REFERENCES Courses(CourseID)
);
And then join all three tables together:
SELECT F.*, FC.*, C.*
FROM Faculty F
INNER JOIN Faculty_Courses FC ON F.FacultyID = FC.FacultyID
INNER JOIN Courses C ON C.CourseID = FC.CourseID
You probably only really want some of the columns from F (Faculty) and C (Courses) and can mostly ignore the FC columns as they are just used for mapping between F and C
I'm surprised your factory_courses table doesn't have a reference to courses.
Either way, with your current query, you are selecting from faculty and courses but aliasing faculty_courses -- you cannot do that. This is what your current query should look like:
select *
from faculty f
join faculty_courses fc on f.facultyid = fc.facultyid
To join the 3rd table, if you had a courseid in your faculty_courses table, perhaps something like this:
select *
from faculty f
join faculty_courses fc on f.facultyid = fc.facultyid
join courses c on fc.courseid = c.courseid
A Visual Explanation of SQL Joins
You're doing a join on two tables without actually linking two columns from those tables; joining Faculty and Courses using columns from Faculty and Faculty_Courses.
Since no column exists with unique values linking Courses with any other table, you cannot perform a join with Courses.
So you should join Faculty and Faculty_courses:
SELECT Faculty.*, Faculty_Courses.*
FROM Faculty INNER JOIN Faculty_Courses
ON Faculty.FacultyID=Faculty_Courses.FacultyID

inner join and partial matches

I'm not sure if the title's correct, so here goes.
The following script returns the correct result: currently Roy Brown teaches Math 101. Roy Brown's TeacherID is 1225, but then they later added the prefix 001- for other purposes.
What I would like to include in the result is some identification that Roy Brown did have another course at one point in time, since Math 101 Old has 1225. It doesn't even have to show how many other courses he had; just something that will let me know that there is more than one row in #coursesCsv. But the result should remain to 2 rows.
What I don't want is to display an extra row for Roy Brown, which is why I'm not doing the commented inner join (ie. right(t.teacherid,4) = right(c.teacherid,4)).
There are no relationships between these two tables since the data in #coursesCSV comes from a csv file.
IF OBJECT_ID('tempdb..#teacher') IS NOT NULL DROP TABLE #teacher
IF OBJECT_ID('tempdb..#coursesCsv') IS NOT NULL DROP TABLE #coursesCsv
create table #teacher
(
TeacherID varchar(10),
FullName varchar(30)
)
insert into #teacher select '001-1225', 'Roy Brown'
insert into #teacher select '001-1230', 'Woody Boyd'
create table #coursesCsv
(
CourseName varchar(30),
TeacherID varchar(10)
)
insert into #coursesCsv select 'Math 101', '001-1225'
insert into #coursesCsv select 'Math 101 Old', '002-1225'
insert into #coursesCsv select 'History 101', '001-1230'
select t.teacherid, c.coursename from
#teacher t inner join #coursesCsv c
on t.teacherid = c.teacherid
--on right(t.teacherid,4) = right(c.teacherid,4)
Do a "Group Count" of courses for the same right(teacherid,4) within a Derived Table (or a Common Table Expression = WITH) and join to it:
select t.teacherid, c.coursename, c.coursecnt
from teacher t
inner join
(
select
teacherid,
coursename,
count(*)
over (partition by right(teacherid,4)) as coursecnt
from coursesCsv
) as c
on t.teacherid = c.teacherid

Select rows that have a specific set of items associated with them through a junction table

Suppose we have the following schema:
CREATE TABLE customers(
id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE items(
id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE customers_items(
customerid INTEGER,
itemid INTEGER,
FOREIGN KEY(customerid) REFERENCES customers(id),
FOREIGN KEY(itemid) REFERENCES items(id)
);
Now we insert some example data:
INSERT INTO customers(name) VALUES ('John');
INSERT INTO customers(name) VALUES ('Jane');
INSERT INTO items(name) VALUES ('duck');
INSERT INTO items(name) VALUES ('cake');
Let's assume that John and Jane have id's of 1 and 2 and duck and cake also have id's of 1 and 2.
Let's give a duck to John and both a duck and a cake to Jane.
INSERT INTO customers_items(customerid, itemid) VALUES (1, 1);
INSERT INTO customers_items(customerid, itemid) VALUES (2, 1);
INSERT INTO customers_items(customerid, itemid) VALUES (2, 2);
Now, what I want to do is to run two types of queries:
Select names of customers who have BOTH a duck and a cake (should return 'Jane' only).
Select names of customers that have a duck and DON'T have a cake (should return 'John' only).
For the two type of queries listed, you could use the EXISTS clause. Below is an example query using the exists clause:
SELECT cust.name
from customers AS cust
WHERE EXISTS (
SELECT 1
FROM items
INNER JOIN customers_items ON items.id = customers_items.itemid
INNER JOIN customers on customers_items.customerid = cust.id
WHERE items.name = 'duck')
AND NOT EXISTS (
SELECT 1
FROM items
INNER JOIN customers_items ON items.id = customers_items.itemid
INNER JOIN customers on customers_items.customerid = cust.id
WHERE items.name = 'cake')
Here is a working example: http://sqlfiddle.com/#!6/3d362/2