How to inner join with multiple tables for 3NF Normalization in SQL - sql

I am trying to create normalization 3 nf (normal form) in this database. However, when I execute the query, the table is empty. As you will see, this is my table and diagram.
Here is the relationship that we want to 3NF
Torder->(Customer-Food-Carrier-Waiter)-(Ternary Relationship)-(Customer_id,Food_id,Carrier_id,Waiter_id)
Here is my query
SELECT
Customers.ID AS CustomerID,
Food.ID AS FoodID,
Carrier.ID AS CarrierID,
Waiter.ID AS WaiterID,
tOrder.ID AS TORDERID
FROM
tOrder
INNER JOIN
Customers ON Customers.ID = tOrder.Customer_id
INNER JOIN
Food ON Food.ID = tOrder.Food_id
INNER JOIN
Carrier ON Carrier.ID = tOrder.Carrier_id
INNER JOIN
Waiter ON Waiter.ID = tOrder.Waiter_id
ORDER BY tOrder.ID;

Clearly, you have an issue where some of the tables are empty or the ids do not match. You can use LEFT JOIN to keep all orders and see what is happening:
FROM tOrder o LEFT JOIN
Customers c
ON c.ID = o.Customer_id LEFT JOIN
Food f
ON f.ID = o.Food_id LEFT JOIN
Carrier ca
ON ca.ID = o.Carrier_id LEFT JOIN
Waiter w
ON w.ID = o.Waiter_id
If there are no matches, then the orders are still in the results, with NULL for the values in the table with no matches.
Note that this also introduces table aliases, so the query is easier to write and to read.

Related

Joining two indirectly related tables without including columns from tables in between

Based on the following ERD, I'm trying to write a query that displays reservations made directly by customers and include reservation id, reservation date, customer id, customer first name, tour trip id, tour category name, tour trip date
I've been able to include everything in my query except for the tour category name because the TOUR_SITES table is in the way. Is there a way I can join the category name from the TOUR_PACKAGE table without adding any extra columns?
SELECT r.RESERVATION_ID, r.RESERVATION_DATE,
c.CUSTOMER_ID, c.FIRST_NAME,t.TRIP_ID, o.TRIP_DATE/*, P.CATEGORY_NAME*/
FROM CUSTOMER c
INNER JOIN RESERVATION r
ON
r.CUSTOMER_ID=c.CUSTOMER_ID
INNER JOIN TOURTRIP_RESERVATION t
ON
r.RESERVATION_ID=t.RESERVATION_ID
INNER JOIN TOUR_TRIP O
ON
t.TRIP_ID=o.TRIP_ID
WHERE AGENT_ID IS NULL;
Is there a way I can join the category name from the TOUR_PACKAGE table without adding any extra columns?
Sure; just join all tables you need. You don't have to add additional columns (the ones you don't need) into the select column list, but you do have to use the tour_sites table anyway.
Something like this:
select r.reservation_id, r.reservation_date,
c.customer_id, c.first_name,t.trip_id, o.trip_date,
p.category_name
from customer c inner join reservation r on r.customer_id=c.customer_id
inner join tourtrip_reservation t on r.reservation_id=t.reservation_id
inner join tour_trip o on t.trip_id=o.trip_id
--
join tour_sites s on s.tour_siteid = o.tour_siteid --> add
join tour_package p on p.category_id = s.category_id --> this
where agent_id is null;
You need to first join tour_sites with tour_trip on TOUR_SITEID and then join tour_package with tour_sites on category_id to get the tour category_name. You can join left join on tour_sites in case there are no tour sites assigned for a tour trip like a newly added tour_trip.
SELECT r.RESERVATION_ID, r.RESERVATION_DATE, c.CUSTOMER_ID, c.FIRST_NAME,t.TRIP_ID, o.TRIP_DATE,TP.CATEGORY_NAME
FROM CUSTOMER c
INNER JOIN RESERVATION r ON r.CUSTOMER_ID=c.CUSTOMER_ID
INNER JOIN TOURTRIP_RESERVATION t ON r.RESERVATION_ID=t.RESERVATION_ID
INNER JOIN TOUR_TRIP O ON t.TRIP_ID=o.TRIP_ID
LEFT JOIN TOUR_SITES TS ON TS.TOUR_SITEID = O.TOUR_SITEID
INNER JOIN TOUR_PACKAGE TP ON TP.CATEGORY_ID = TS.CATEGORY_ID
WHERE AGENT_ID IS NULL;

How can I connect multiple tables using an inner join?

I am trying to select and print customer_name(customer table), room_number(room table), room_rate (room table),rate_type(reservation table), checkin_date(reservation table), checkout_date(reservation table), billing_amount(billing table) from multiple tables. I tried to do an inner join but the error is 'column ambiguously defined '
SELECT *
FROM Customer c
INNER JOIN Reservation r
ON c.customer_id = r.customer_id
INNER JOIN Room s
ON s.room_id = r.room_id
INNER JOIN billing b
ON reservation_id = b.reservation_id
WHERE c.customer_name = 'John Scott';
You're missing the table alias on the left side of the last JOIN. Try this:
SELECT *
FROM Customer c
INNER JOIN Reservation r
ON c.customer_id = r.customer_id
INNER JOIN Room s
ON s.room_id = r.room_id
INNER JOIN billing b
ON r.reservation_id = b.reservation_id
WHERE c.customer_name = 'John Scott';
One problem is in your join and one in your select:
SELECT *
-------^ duplicate column names
FROM Customer c INNER JOIN
Reservation r
ON c.customer_id = r.customer_id INNER JOIN
Room s
ON s.room_id = r.room_id INNER JOIN
billing b
ON reservation_id = b.reservation_id
--------^ missing table alias
WHERE c.customer_name = 'John Scott';
If the only duplicate columns are the JOIN keys, then the USING clause (standard but not supported by all databases) is a handy way to get all the columns:
SELECT *
FROM Customer c INNER JOIN
Reservation r
USING (customer_id) INNER JOIN
Room s
USING (room_id) INNER JOIN
billing b
USING (reservation_id)
WHERE c.customer_name = 'John Scott';
When you using USING, the key columns are not duplicated with SELECT *.

Understand Sub-Queries

I was initially looking to see a breakdown of the total dollar business that each vendor has done (indirectly via the distributor) with each customer, where I'm trying not to use the Inner Join Syntax and used the Query below for this purpose:
select customers.cust_id, Vendors.vend_id, sum(quantity*item_price) as total_business from
(((Vendors left outer join Products
on Products.vend_id = Vendors.vend_id)
left outer join OrderItems --No inner joins allowed
on OrderItems.prod_id = Products.prod_id)
left outer join Orders
on Orders.order_num = OrderItems.order_num)
left outer join Customers
on Customers.cust_id = Orders.cust_id
where Customers.cust_id is not null -- THE ONLY DIFFERENCE BETWEEN QUERY1 AND QUERY2
group by Customers.cust_id, Vendors.vend_id
order by total_business
Now, I am trying to see the query output results for all vendor-customer combinations, including those combinations where there was no business transacted and am trying to write this via a single SQL Query. My teacher provided this solution, but I honestly cannot understand the logic at all, as I've never come across Sub-queries.
select
customers.cust_id,
Vendors.vend_id,
sum(OrderItems.quantity*orderitems.item_price)
from
(
customers
inner join
Vendors on 1 = 1
)
left outer join --synthetic product using joins
(
orders
join
orderitems on orders.order_num = OrderItems.order_num
join
Products on orderitems.prod_id = products.prod_id
) on
Vendors.vend_id = Products.vend_id and
customers.cust_id = orders.cust_id
group by customers.cust_id, vendors.vend_id
order by customers.cust_id
Thanks a lot
I would write this query as:
select c.cust_id, v.vend_id, coalesce(cv.total, 0)
fro Customers c cross join
Vendors v left outer join
(select o.cust_id, v.vend_id, sum(oi.quantity * oi.item_price) as total
from orders o join
orderitems oi
on o.order_num = oi.order_num join
Products p
on oi.prod_id = p.prod_id
group by o.cust_id, v.vend_id
) cv
on cv.vend_id = v.vend_id and
cv.cust_id = c.cust_id
order by c.cust_id;
The structure is quite similar. Both version start by creating a cross product between all customers and vendors. This creates all the rows in the output result set. Next, the aggregation needs to be calculated at this level. In the above query, this is done explicitly as a subquery which aggregates the values to the customer/vendor level. (In the original query, this is done in the outer query.)
The final step is joining these together.
Your teacher should be encouraging you to use table aliases, particularly table abbreviations. You should also be encouraged to use the proper join. So, although you can express a cross join as an inner join with on 1=1, a cross join is part of the SQL language, not a hack.
Similarly, parentheses in the from clause can make the logic harder to follow. Explicit subqueries are more easily read.

SQL Query on SQL Server 2008

I'm trying to get only customers that ordered both a "Gas Range" and a "Washer". I'm getting Customers who ordered a "Gas Range" and not a "Washer" and customers with both. I need the customer that meets both conditions. I'm close but a little stuck. Below is the query that I have so far. Please let me know if you need more information.
My Tables - CUSTOMER(CUST_NUM, CUST_NAME), ORDER_LINE(ORDER_NUM, PART_NUM), ORDERS(ORDER_NUM, CUST_NUM), PART(PART_NUM, PART_DESCRIPTION)
SELECT C.CUST_NAME AS [Customer(s) that ordered a Gas Range and Washer]
FROM CUSTOMER C
INNER JOIN ORDERS O
ON C.CUST_NUM = O.CUST_NUM
INNER JOIN ORDER_LINE OL
ON O.ORDER_NUM = OL.ORDER_NUM
INNER JOIN PART P
ON OL.PART_NUM = P.PART_NUM
WHERE P.PART_DESCRIPTION IN ('GasRange','Washer')
GROUP BY C.CUST_NAME
try the following
SELECT C.CUST_NAME AS [Customer(s) that ordered a Gas Range and Washer]
FROM CUSTOMER C
INNER JOIN ORDERS O
ON C.CUST_NUM = O.CUST_NUM
INNER JOIN ORDER_LINE OL
ON O.ORDER_NUM = OL.ORDER_NUM
INNER JOIN PART P
ON OL.PART_NUM = P.PART_NUM
INNER JOIN ORDERS O2
ON C.CUST_NUM = O2.CUST_NUM
INNER JOIN ORDER_LINE OL2
ON O2.ORDER_NUM = OL2.ORDER_NUM
INNER JOIN PART P2
ON OL2.PART_NUM = P2.PART_NUM
WHERE P.PART_DESCRIPTION IN ('GasRange') and P2.PART_DESCRIPTION IN ('Washer')
GROUP BY C.CUST_NAME
EDIT: Had a further look and I'm afraid that this can't be simplified in any other way than using WITH and complicated aggregate functions, which I would say would be more complicated than this - I think the other solution suggested using WITH won't work - it joins incorrectly. You definitely can't remove order line, and you have to use the order twice as well - if it was used once, it will cover only when the customer ordered it within one order, which is not what you wanted ;)
Try this...
So basically you need to join your Parts table again to ensure the same customer ordered a "Gas Range" and a "Washer". An IN, like in your current query functions as an OR therefore you are not getting the expected result.
WITH CTE AS (
SELECT DISTINCT O.CUST_NUM FROM ORDERS O
INNER JOIN ORDER_LINE OL
ON O.ORDER_NUM = OL.ORDER_NUM
INNER JOIN PART P
ON OL.PART_NUM = P.PART_NUM
INNER JOIN PART P2
ON OL.PART_NUM = P2.PART_NUM
WHERE P.PART_DESCRIPTION IN ('GasRange')
AND P2.PART_DESCRIPTION IN ('Washer')
)
SELECT C.CUST_NAME AS [Customer(s) that ordered a Gas Range and Washer]
FROM CUSTOMER C
INNER JOIN CTE O
ON C.CUST_NUM = O.CUST_NUM

Outer Join or NOT IN

Need a little help solving problem. Apologies if this is vague which is why I need help. Show The title ID, title, publisher id, publisher name, and terms for all books that have never been sold. This means that there are no records in the Sales Table. Use OUTER JOIN or NOT IN against SALES table. In this case, the terms are referring to the items on the titles table involving money. ( Price, advance, royalty ).
I've come up with the following code and there are no records returned. I've looked at the tables and no records should be returned. Not sure my code is correct based on using the OuterJoin or NOT IN. Thanks!
SELECT titles.title_id,
titles.title,
publishers.pub_id,
sales.payterms,
titles.price,
titles.advance,
titles.royalty
FROM publishers
INNER JOIN titles
ON publishers.pub_id = titles.pub_id
INNER JOIN sales
ON titles.title_id = sales.title_id
WHERE (sales.qty = 0)
I believe all you need it to change the INNER JOIN to a LEFT JOIN for your sales table. Then check for NULL on any of the columns from the sales table.
SELECT titles.title_id, titles.title, publishers.pub_id, sales.payterms,
titles.price, titles.advance, titles.royalty
FROM publishers INNER JOIN
titles ON publishers.pub_id = titles.pub_id LEFT JOIN
sales ON titles.title_id = sales.title_id
WHERE sales.qty IS NULL
Well, you are using INNER JOIN so it will only return records that match the join condition which is not what you want. You should use LEFT OUTER JOIN (or LEFT JOIN) and check that the sales side is null (meaning there's no record matching).
SELECT titles.title_id, titles.title, publishers.pub_id, sales.payterms,
titles.price, titles.advance, titles.royalty
FROM publishers INNER JOIN
titles ON publishers.pub_id = titles.pub_id LEFT OUTER JOIN
sales ON titles.title_id = sales.title_id
WHERE sales.title_id IS NULL
IS NULL Means that a data value does not exist in the database. And just change your INNER JOIN TO LEFT JOIN.
use this..
SELECT titles.title_id,
titles.title,
publishers.pub_id,
sales.payterms,
titles.price,
titles.advance,
titles.royalty
FROM publishers
INNER JOIN titles
ON publishers.pub_id = titles.pub_id
LEFT JOIN sales
ON titles.title_id = sales.title_id
WHERE (sales.qty IS NULL)