What's the right kind of join? - sql

I have two tables:
Products
Id Integer
Name Character Varying(200)
Orders
Id Integer
Product_Id integer
Started Timestamp
I need to fetch all orders with the product id and name together.
I've tried using inner join like this:
select orders.id, orders.started, products.id, products.name
from orders inner join
products
on products.id = order.product_id
But it don't show some orders that have no product linked (service orders).
What i'm doing wrong?

When you don't always have elements on the second table, you should use the "left join" -> it will retrive all rows from the left table, even if the right table doesnt have a matching row.
Like this:
select orders.id, orders.started, products.id, products.name from orders
left join products on products.id = order.product.id

You would need to do a LEFT JOIN on your Product table from the Orders table.
SELECT [Your Columns]
FROM Orders o
LEFT OUTER JOIN Products p ON o.ProductID = p.ID
A left join will still retrieve all rows from Orders, and will retrieve the data from the Product table when there is a match. When there is no ProductID in the Products table, the data you're trying to pull from the Products table will simply be NULL.
Using an inner join requires that the column(s) joined on be present in both tables. For example, if you have a Orders.ProductID = 7, Products must have a row with ID = 7 otherwise your query won't return a row for that Order.

Related

SQL how to count the number of relations between two tables and include zeroes?

I have a table of orders, and a table of products contained in these orders. (The products-table has order_id, a foreign key referring to orders.id).
I would like to query the number of products contained in each order. However, I also want orders to be contained in the results if they do not contain any products at all.
This means that a simple
SELECT *, COUNT(*) n_products FROM `orders` INNER JOIN `products` on `products.order_id` = `orders.id` GROUP_BY `order_id`
does not work, since orders without any products disappear.
Using a LEFT OUTER JOIN instead would add rows without product-information, but the distinction between an order with 1 product and an order with 0 products is lost.
What am I missing here?
You need a left join here, and you should be counting some column from the products table:
SELECT
o.*,
COUNT(p.order_id) AS n_products
FROM orders o
LEFT JOIN products p
ON p.order_id = o.id
GROUP BY
o.id;
Note that I assume that Postgres would allow grouping by orders.id and then selecting all columns from that table. If not, then you would only be able to select o.id in addition to the count.

How to avoid the "ambiguous" error message when using joins to create several tables

I'm trying to use the following code to create a list of customers and their brands that they buy. The brands table has the brand name and customer_id is in the customers table. To link them I have to get the brand_id and receipt_id linked together via the receipts table (connects to customers table) and receipt_item_details1 table (connects to brands table).
So, receipt_item_details1 table (has brand_id column to then connect to brands table) and new table customer_receipts (created by first inner most subquery) are trying it to be linked by receipt_id. I'd like to show the customer_id column when I build my table joining these two table (an original: receipt_item_details1 joined to a new table: customer_receipts).
ISSUE: I keep getting the following error. how do Infix it and also list the brands?
"column reference "customer_id" is ambiguous
LINE 3: ...pts.receipt_id, receipt_item_details1.receipt_id, customer_r.."
SELECT customer_brandids.brand_id, brands.brand_id, customer_brandids.customer_id, brands.brand_name
FROM
(SELECT customer_receipts.receipt_id, receipt_item_details1.receipt_id, customer_receipts.customer_id, receipt_item_details1.brand_id
FROM
(SELECT receipts.customer_id, customers.customer_id, receipts.receipt_id
FROM receipts
INNER JOIN customers
ON receipts.customer_id = customers.customer_id) AS customer_receipts
INNER JOIN receipt_item_details1
ON customer_receipts.receipt_id = receipt_item_details1.receipt_id) AS customer_brandids
INNER JOIN brands
ON customer_brandids.brand_id = brands.brand_id
Your inner subselect
(SELECT receipts.customer_id, customers.customer_id
generates a result with two columns named customer_id. So your next higher subselect cannot differ between both columns if you reference customer_id
You should give one or both an alias:
(SELECT receipts.customer_id as r_customer_id,
customers.customer_id as c_customer_id
Then your next higher query can call
SELECT customer_receipts.c_customer_id...
So first step of solving the problem:
SELECT
customer_brandids.brand_id,
brands.brand_id,
customer_brandids.c_customer_id, --> reference alias
brands.brand_name
FROM
(SELECT
customer_receipts.receipt_id as c_receipt_id, --> same problem
receipt_item_details1.receipt_id as r_receipt_id,
customer_receipts.c_customer_id, --> reference alias
receipt_item_details1.brand_id
FROM
(SELECT
receipts.customer_id as r_customer_id, --> here was the problem
customers.customer_id as c_customer_id,
receipts.receipt_id
FROM receipts
INNER JOIN customers
ON receipts.customer_id = customers.customer_id) AS customer_receipts
INNER JOIN receipt_item_details1
ON customer_receipts.receipt_id = receipt_item_details1.receipt_id) AS customer_brandids
INNER JOIN brands
ON customer_brandids.brand_id = brands.brand_id
Addionally:
You don't need to take both columns (e.g. of receipt_id) because of the INNER JOIN it is ensured that both columns have the same value
You can use aliases to shorten your query.
You don't need to create a subquery for each join. Just join.
All in all, this should do the same:
SELECT b.brand_id, c.customer_id, b.brand_name
FROM receipts r
INNER JOIN customers c ON r.customer_id = c.customer_id
INNER JOIN receipt_item_details1 rid ON r.receipt_id = rid.receipt_id
INNER JOIN brands b ON b.brand_id = rid.receipt_id
demo: db<>fiddle
Do not use nested selects when it is not necessary, try to use joins, and query will be more simple and will look something like this
select * from receipts
join customers on receipts.customer_id = customers.customer_id
join receipt_item_details1 on receipts.receipt_id = receipt_item_details1.receipt_id
join brands on receipt_item_details1.brand_id = brands.brand_id
Instead of asterisk you can define columns you want to get

SQL Beginner: Getting items from 2 tables (+grouping+ordering)

I have an e-commerce website (using VirtueMart) and I sell products that consist child products. When a product is a parent, it doesn't have ParentID, while it's children refer to it. I know, not the best logic but I didn't create it.
My SQL is very basic and I believe I ask for something quite easy to achieve
Select products that have children.
Sort results by prices (ASC/DSC).
SELECT * FROM Products INNER JOIN Prices ON Products.ProductID = Prices.ProductID ORDER BY Products.Price [ASC/DSC]
Explanation:
SELECT - Select (Get/Retrieve)
* - ALL
FROM Products - Get them from a DB Table named "Products".
INNER JOIN Prices - Selects all rows from both tables as long as there is a match between the columns in both tables. Rather, JOIN DB Table "Products" with DB Table "Prices".
ON - Like WHERE, this defines which rows will be checked for matches.
Products.ProductID = Prices.ProductID - Your match criteria. Get the rows where "ProductID" exists in both DB Tables "Products" and "Prices".
ORDER BY Products.Price [ASC/DSC] - Sorting. Use ASC for Ascending, DSC for Descending.
This table design is subpar for a number of reasons. First, it appears that the value 0 is being used to indicate lack of a parent (as there's no 0 ID for products). Typically this will be a NULL value instead.
If it were a NULL value, the SQL statement to get everything without a parent would be as simple as this:
SELECT * FROM Products WHERE ParentID IS NULL
However, we can't do that. If we make the assumption that 0 = no parent, we can do this:
SELECT * FROM Products WHERE ParentID = 0
However, that's a dangerous assumption to make. Thus, the correct way to do this (given your schema above), would be to compare the two tables and ensure that the parentID exists as a ProductID:
SELECT a.*
FROM Products AS a
WHERE EXISTS (SELECT * FROM Products AS b WHERE a.ID = b.ParentID)
Next, to get the pricing, we have to join those two tables together on a common ID. As the Prices table seems to reference a ProductID, we can use that like so:
SELECT p.ProductID, p.ProductName, pr.Price
FROM Products AS p INNER JOIN Prices AS pr ON p.ProductID = pr.ProductID
WHERE EXISTS (SELECT * FROM Products AS b WHERE p.ID = b.ParentID)
ORDER BY pr.Price
That might be sufficient per the data you've shown, but usually that type of table structure indicates that it's possible to have more than one price associated with a product (we're unable to tell whether this is true based on the quick snapshot).
That should get you close... if you need something more, we'll need more detail.
use the below script if you are using ssms.
SELECT pd.ProductId,ProductName,Price
FROM product pd
LEFT JOIN price pr ON pd.ProductId=pr.ProductID
WHERE EXISTS (SELECT 1 FROM product pd1 WHERE pd.productID=pd1.ParentID)
ORDER BY pr.Price ASC
Note :neither of your parent product have price in price table. If you want the sum of price of their child product use the below script.
SELECT pd.ProductId,pd.ProductName,SUM(ISNULL(pr.Price,0)) SUM_ChildPrice
FROM product pd
LEFT JOIN product pd1 ON pd.productID=pd1.ParentID
LEFT JOIN price pr ON pd1.ProductId=pr.ProductID
GROUP BY pd.ProductId,pd.ProductName
ORDER BY pr.Price ASC
You will have to use self-join:
For example:
SELECT * FROM products parent
JOIN products children ON parent.id = children.parent_id
JOIN prices ON prices.product_id = children.id
ORDER BY prices.price
Because we are using JOIN it will filter out all entries that don't have any children.
I haven't tested it, I hope it would work.

Access: Find partial duplicates, show only one and sum a value

I have a database for my invoices. I would like to make a form where I list all purchases from one customer. If the customer bought the same item more than once I'd like to show that item only once and sum the quantity field. I'd like to find an SQL solution, but I'm a bit stuck.
This is the query in my form:
SELECT Order.fkCustID, Order.OrderID, OrderLine.fkProductID, OrderLine.Qty, ProductList.Item
FROM ProductList INNER JOIN
([Order] INNER JOIN
OrderLine
ON Order.OrderID = OrderLine.fkOrderID
) ON ProductList.ProductID = OrderLine.fkProductID
ORDER BY ProductList.Item;
The OrderLine table contains the individual products bought, the Order table links these to my customer table. ProductList contains the product description.
So where fkCustID and fkProductID are the same, I'd like to only show the oldest entry and sum the Qty field of both.
I think you just want an aggregation query, that looks something like this:
SELECT ol.fkProductID, SUM(ol.Qty) as qty, pl.Item
FROM ProductList as pl INNER JOIN
([Order] as o INNER JOIN
OrderLine as ol
ON o.OrderID = ol.fkOrderID
)
ON pl.ProductID = ol.fkProductID
WHERE o.fkCustID = ???
GROUP BY ol.fkProductID, pl.Item
ORDER BY pl.Item;

Find records that do not have related records in SQL

I have 2 tables (Orders, OrderItems) that are related based on a column OrderID. I need to find all Orders that do not have any OrderItems.
We use JOIN to find related data. To find data without any related data, we can use an anti-join.
The following joins the tables, then selects those without any order items. This tends to be more efficient that a WHERE id NOT IN (...) style query.
select *
from
Orders O
left outer join OrderItems I
on I.OrderId = O.Id
where
I.Id is null
Select * From Orders Where OrderID not in (Select Distinct OrderID From OrderItems)
try with LEFT EXCEPTION JOIN
select *
from Orders
LEFT EXCEPTION JOIN OrderItems ON ...