SQL Query help - Joins - sql

I have a couple of SQL tables which are:
customer(Id, Name, Address, PhoneNumber);
station (Id, City, Country, Location);
car(Id, Reg, Type, Milage);
contract(CustId, StationId, CarId);
I need to use these tables to find all customers who have rented a BMW. I have written out my query as:
SELECT *
FROM CUSTOMER, CAR, CONTRACT
WHERE CUSTOMER.ID = CONTRACT.CUSTID
AND CAR.TYPE = "BMW";
Would that be correct? My thinking was I need to join the tables as there is no way of knowing what customer has rented which car, but I feel like I might be doing something wrong? Any help would be appreciated.

No, your query is not correct -- it is incorrect logically and it is written poorly.
The better way to write the query is to use proper, explicit, standard JOIN syntax:
SELECT *
FROM CONTRACT co JOIN
CUSTOMER cu
ON co.CUSTID = cu.ID JOIN
CAR ca
ON co.CARID = ca.ID -- this is a guess
WHERE ca.TYPE = 'BMW';
Notes:
You are missing the JOIN condition between CONTRACT and CAR (presumably). With explicit JOIN syntax, this is pretty obvious.
I am guessing at the right JOIN condition for CAR.
Use single quotes for string constants. That is the SQL standard, although some databases do allow double quotes as well.
SELECT * is usually to be discouraged. It is generally better practice to list the columns, especially in this case when the same column name might appear in different tables.

SELECT co.Id as ContractId , Name, Address, PhoneNumber,
cu.Id as CustId , City, Country, Location,
ca.Id as CarId, Reg, Type, Milage,
FROM CONTRACT co JOIN
CUSTOMER cu
ON co.CUSTID = cu.ID JOIN
CAR ca
ON co.CARID = ca.ID
STATION st
ON co.StationId = st.ID
WHERE ca.TYPE = 'BMW';
You need to mention Column names explicitly because there will be name conflict for field Id which is common across many tables
PS: Just modifying query from Gordon Linoff answer on the same question

This should help: (You do not need to worry about Station as per your requirement)
SELECT distinct cus.*
FROM CONTRACT con
JOIN CUSTOMER cus ON con.CUSTID = cus.ID
JOIN CAR car ON co.CARID = car.ID
WHERE car.TYPE = 'BMW';

Related

ORA-00957: duplicate column name ERROR While Creating View

I am getting ORA-00957: duplicate column name error while trying to create view. What is the problem ? How can I fix my query?
CREATE VIEW SIGN
AS
SELECT
CONTRACTS.CONTRACT_ID, CONTRACTS.PERSONNEL_ID, CONTRACTS.CUSTOMER_ID,
CUSTOMERS.FIRST_NAME,
PERSONNEL.FIRST_NAME, CUSTOMERS.CUSTOMER_ID, PERSONNEL.PERSONNEL_ID
FROM
CONTRACTS, CUSTOMERS, PERSONNEL
WHERE
(CONTRACTS.PERSONNEL_ID = PERSONNEL.PERSONNEL_ID)
AND (CONTRACTS.CUSTOMER_ID = CUSTOMERS.CUSTOMER_ID)
First, learn to use proper, explicit, standard, readable JOIN syntax.
Second, views cannot have duplicates names, such as PERSONNEL_ID and FIRST_NAME in your view.
Third, table aliases make the query easier to write and read.
So:
CREATE VIEW SIGN AS
SELECT c.CONTRACT_ID, c.PERSONNEL_ID, c.CUSTOMER_ID,
cu.FIRST_NAME,
p.FIRST_NAME as PERSONNEL_FIRST_NAME
FROM CONTRACTS C JOIN
CUSTOMERS CU
ON C.CUSTOMER_ID = CU.CUSTOMER_ID JOIN
PERSONNEL P
ON C.PERSONNEL_ID = p.PERSONNEL_ID;
Column names in a result set must be unique. Qualifying them with a table name does not make the column names any different, so
SELECT CONTRACTS.PERSONNEL_ID, PERSONNEL.PERSONNEL_ID...
will fail because PERSONNEL_ID is specified twice.
The following should work as you expect:
CREATE VIEW SIGN AS
SELECT co.CONTRACT_ID,
co.PERSONNEL_ID,
cu.CUSTOMER_ID,
p.FIRST_NAME
FROM CONTRACTS co
INNER JOIN CUSTOMERS cu
ON cu.CUSTOMER_ID = co.CUSTOMER_ID
INNER JOIN PERSONNEL p
ON p.PERSONNEL_ID = co.PERSONNEL_ID

How to obtain all the tuples after a certain date in sql?

I have to obtain the male employee with highest number of requests in the second half of April 2014.
I have these tables:
Employee (EmployeeID, firstName, LastName, gender)
Workplace (CompanyID, EmployeeID, CompanyName)
Extras (ExtraID, CompanyID, Requests, Description, Date)
Extras.Requests is a string, not numerical.
My SQL attempt looks like this:
SELECT
Employee.FirstName, Employee.LastName,
SUM(COUNT(Extras.ExtraID)
FROM
Employee
INNER JOIN
(Workplace
INNER JOIN
Extras ON Workplace.CompanyID = Extras.CompanyID)
ON Workplace.EmployeeID = Employee.EmployeeID
WHERE
Employee.Gender = "male"
AND Extras.Date BETWEEN #4/15/2014# AND #4/30/2014#
SORT BY
SUM(COUNT(Extras.ExtraID) DESC;
LIMIT 1;
I'm not sure if my query is correct or not, thanks in advance.
There are several issues with your querySUM(COUNT(...)) nesting aggregate functions like this isn't permitted
You also need a GROUP BY clause to use aggregation function with non-aggregating columns (which are Employee.FirstName, Employee.LastName in your query).
Sorting is performed by an ORDER BY clause
Your original query includes a nested inner join which is likely to produce unexpected results.
FROM Employee
INNER JOIN(Workplace
INNER JOIN Extras ON Workplace.CompanyID=Extras.CompanyID
) ON Workplace.EmployeeID=Employee.EmployeeID
While nesting joins is allowed it is rarely used, I suggest you avoid it.
I would expect the query to look more like this
SELECT
Employee.FirstName, Employee.LastName, COUNT(Extras.ExtraID)
FROM ((Employee
INNER JOIN Workplace
ON Workplace.EmployeeID = Employee.EmployeeID)
INNER JOIN Extras
ON Workplace.CompanyID = Extras.CompanyID)
WHERE Employee.Gender = "male"
AND Extras.Date BETWEEN #4/15/2014# AND #4/30/2014#
GROUP BY
Employee.FirstName
,Employee.LastName
ORDER BY
COUNT(Extras.ExtraID) DESC;
LIMIT 1;
It's been years since I used access, I think it still wants parentheses in the from clause as I've shown above. In most SQL implementation they are not required, and it is more common for literals to use single quotes e.g. WHERE Employee.Gender = 'male'.

SQL select for many to many relationship using binding table

I am struggling with writing a select for diagram on the picture.
What I want to do is write a select, which will show me details of a car repair. As you can see in table Repairs there are only 2 attributes but I am not sure if it's necessary to add more, especialy those from employees_list and parts_list, since I want to show repair data for every vehicle by it's plate_number. By repair data I mean repair id, vehicle plate_number, all employees working on the repair and all parts used on the repair. If my diagram is wrong, please help me fix it and I have no idea how to write select for this because of the many to many relation and the use of binding tables.
This is not so hard as it may seem.
First, obviously, we have to select cars:
select vehicles.* from vehicles
then, let's join repairs:
select
vehicles.*
from vehicles
inner join repairs on vehicles.id = repairs.vehicle.id
We don't need data from repairs in resule set, so we just join it but not mention in 'select' part.
Then we have to join parts needed for repair, and info about parts itself:
select
vehicles.*
from vehicles
inner join repairs on vehicles.id = repairs.vehicle.id
inner join parts_list on parts_list.repair_id = repairs.id
inner join parts on parts_list.part_id = parts.id
For that query we get amout of rows equivalent of amount of parts needed for repair. But it would be more easy to handle such data in code if we aggregate all of them into json column. So in result set we willsee something like:
vehicle_id, vehicle_part, parts_needed_as_json
Lets aggregate this:
select
vehicles.*, json_agg(parts.*) as parts_needed
from vehicles
inner join repairs on vehicles.id = repairs.vehicle_id
inner join parts_list on parts_list.repair_id = repairs.id
inner join parts on parts_list.part_id = parts.id
group by vehicles.id, repairs.id
Now you can add same logic for employees:
select
vehicles.*,
json_agg(parts.*) as parts_needed,
json_agg(employes.*) as employees_needed
from vehicles
inner join repairs on vehicles.id = repairs.vehicle.id
inner join parts_list on parts_list.repair_id = repairs.id
inner join parts on parts_list.part_id = parts.id
inner join employees_list on employes_list.repair_id = repairs.id
inner join employees on employees_list.employee_id = employees.id
group by vehicles.id, repairs.id
BTW, I suggest you to rename your tables to lowercase and singulars.
Like: 'repair', 'employee' and 'vehicle';
Also, name your binding tables like: 'repair_part' and 'repair_employee'.
Some people even suggest to arrange related tables in that names by alphabet, like: 'employee_repair' and 'part_repair', but I think it's not required;
Maybe this is a question of taste but in most cases this leads to more readable queries.
I.e, the query above will looks like:
select
vehicle.*,
json_agg(part.*) as parts_needed,
json_agg(employee.*) as employees_needed
from vehicle
inner join repair on vehicle.id = repair.vehicle_id
inner join parts_repair on parts_repair.repair_id = repair.id
inner join part on parts_repair.part_id = part.id
inner join employees_repair on employees_repair.repair_id = repair.id
inner join employee on employees_repair.employee_id = employee.id
group by vehicle.id, repair.id
Note how elegant 'on' conditions looks now: parts_repair.part_id = part.id, parts_repair.part_id = part.id
Sorry for bad english

SQL: Selecting multiple columns from multiple tables

I am using MS Access 2013.
I am trying to selecting the number and name from Salesperson table. Number, name and postcode from Customers table as well as all the information from the CarSale table all within the past month and order by salesperson no.
I have come up with the following
SELECT CS.carNo, CS.dateOfSale, SA.salespersonNo, SA.name AS SalesName,
CU.customerNo, CU.name AS CustName, CU.postCode
FROM CarSale AS CS, Car AS C, Salesperson AS SA, Customer AS CU
WHERE CS.carNo = C.carNo AND CS.salespersonNo = SA.salespersonNo
AND CS.customerNo = CU.customerNo AND dateOfSale BETWEEN #01/09/2016#
AND #02/09/2016#
ORDER BY CS.salespersonNo;
However as you can see, this is butt-ugly! I did some research and found that I should be using "JOINS" so I went ahead and included them, this is where my problem starts.
After inserting the JOINS into the query I get something that looks like this:
SELECT CS.carNo, CS.dateOfSale, SA.salespersonNo, SA.name AS SalesName,
CU.customerNo, CU.name AS CustName, CU.postCode
FROM CarSale AS CS
JOIN Car AS C ON CS.carNo = C.carNo
JOIN Salesperson AS SA on CS.salespersonNo = SA.salespersonNo
JOIN Customer AS CU ON CS.customerNo = CU.customerNo
WHERE cs.dateOfSale BETWEEN #01/09/2016# AND #02/09/2016#
ORDER BY CS.salespersonNo;
Here are the tables:
**CarSale**
carNo salespersonNo customerNo dateOfSale
-------------------------------------------------------
**Salesperson**
salespersonNo name contactNo monthlySalary centreNo
--------------------------------------------------------------
**Customer**
customerNo name contactNo postCode
---------------------------------------------
The error I am getting is "Syntax error in FROM clause."
I think you're close, but there is something wonky about your JOINs - you have a join on 'Car', but that's not one of your tables. JOINing occurs between tables, with ON specifying the fields that are equivalent (what you are JOINing ON). With that in mind:
SELECT s.salespersonNo, s.name, c.customerNo, cs.carNo,
cs.dateofsale, c.name, c.postCode
FROM salesperson s
INNER JOIN carsale cs
ON cs.salespersonNo = s.salespersonNo
INNER JOIN customer c
ON cs.customerNo = c.customerNo
WHERE cs.dateOfSale BETWEEN #01/09/2016# AND #02/09/2016#
ORDER BY CS.salespersonNo;
Notice that your WHERE and ORDER BY are unchanged, and I just used different aliases in my test run. The main difference is in the JOIN - I join from salesperson to CarSales ON the salespersonNo, and then from CarSales to customerNo, similar to what you already have.
The syntax error is because with multiple JOINs you need parentheses around every pair of them.
It would be a lot easier to use the query designer, it does those things automatically.
SELECT CS.carNo, CS.dateOfSale, SA.salespersonNo, SA.name AS SalesName,
CU.customerNo, CU.name AS CustName, CU.postCode
FROM (((CarSale AS CS
INNER JOIN Car AS C ON CS.carNo = C.carNo)
INNER JOIN Salesperson AS SA on CS.salespersonNo = SA.salespersonNo)
INNER JOIN Customer AS CU ON CS.customerNo = CU.customerNo)
WHERE cs.dateOfSale BETWEEN #01/09/2016# AND #02/09/2016#
ORDER BY CS.salespersonNo;
As Stidgeon wrote, if these are all fields you need, you can omit the join with Car.

SQL join beween tables with if statement

Say, I have the following two tables:
Table customer:
id, salutation, forename, lastname, companyID
and a table company:
Company_id, Company_name, Company_address
and I want to have an evaluation over all users and their company (if they belong to one)
salutation, forename, lastname, companyName
that would amount basically to a very easy script:
select salutation, forename, lastname, company_name
from customer, company
where companyID=Company_id;
The trouble now is just, that companyID can be null. (A customer doesn't need to be part of a company). And since there is no companyID null entry in the company table and any customer who has no company ID listed is omitted due to the joint statement.
Of couse I could divide it into two scripts one for companyid=null and one for not null and mix them with a UNION command, but is there perhaps something like an if statement?
something like:
select salutation, forename, lastname, placeholder
from customer, company
where
if companyID=null then placeholder=null
else (companyID=Company_id and placeholder=company_name);
?
I know there is a case statement, that can check on the field's value and return something else instead, but is there a way to combine that with a joint to another table?
You are looking for an outer join:
select cu.salutation, cu.forename, cu.lastname, co.company_name
from customer cu
left join company co on cu.companyID = co.Company_id;
In general you should stop using the ancient implicit join syntax in the where clause and use an explicit JOIN operator. That is also the only cross-DBMS way to actually do an outer join (all DBMS that supported some proprietary outer join syntax have deprecated that)
Try this
select salutation, forename, lastname, placeholder
from customer, company
where
(companyID=null and placeholder=null )
OR
(companyID=Company_id and placeholder=company_name);
Use a left join instead of an inner join
select a.salutation, a.forename, a.lastname, a.company_name
from customer a
left outer join company b
on a.companyID=b.companyID;