Using a where statement against left outer join - sql

I have a query that returns ALL the appointments for an advisor in a week, and what items they sold at that appointment (if non have been sold it still shows the appointment) I have done this by using a left outer join on the appointments table and ithe items table.
In this scenario I only want to display sold items in stock ( in stock is a field in the items table)
If I use a 'where instock true' against items, I loose all the appointments where no items have been sold ?
Do I need to nest these queries somehow - if so how ? Thanks

The key to what you want to do is to move the condition on the stock to the on clause from the where clause. Presumably, your existing query looks something like this:
select <whatever>
from appointments a left join
sells s
on a.appointmentid = s.appointmentid
where s.instock = 1;
The where clause is turning the left join into an inner join. When there is no match, the value of instock is NULL, which does not match 1. The solution is to move the condition to the on clause:
select <whatever>
from appointments a left join
sells s
on a.appointmentid = s.appointmentid and
s.instock = 1;

You should use ON instead WHERE. That way, if the items aren't in stock the wont match and the right part of your join will be NULL.
SELECT A.*, S.* FROM appointments A LEFT JOIN sells S ON ... AND sells.instock = 1;
I could be more precise if I had the SQL you've tried to write.

Related

LEFT JOIN with condition in WHERE clause

SELECT
supplies.id,
supplierId,
supplies.date,
supplies.commodity,
supplier_payments.date AS paymentDate,
FROM
supplies
INNER JOIN suppliers ON suppliers.id = supplies.supplierId
LEFT JOIN supplier_payments ON supplier_payments.supplyId = supplies.id
WHERE supplier_payments.isDeleted = 0 AND supplierId = 1
What I am trying is to get all records from supplies table and related records from supplier_payments but the supplier_payments.isDeleted should be equal to 0. What happens now that I only get records from supplies that have at least one supplier payment because of the condition. Is there a way to get all supply records and supply payments with condition?
Consider moving the condition on the LEFT JOINed table to the ON clause of the JOIN:
SELECT
sr.id,
se.supplierId,
se.date,
se.commodity,
sp.date AS paymentDate,
FROM supplies se
INNER JOIN suppliers sr ON sr.id = se.supplierId
LEFT JOIN supplier_payments sp
ON sp.supplyId = se.id
AND sp.isDeleted = 0
WHERE se.supplierId = 1
Side notes:
in a multi-table query, always qualify each column with the table it belongs to, to make the query easier to follow and avoid ambiguity
table aliases make the query easier to read and write

SQL: Left Join with three tables

I have three tables
Products (idProduct, name)
Invoices(typeinvoice, numberinvoice, date)
Item-invoices(typeinvoice, numberinvoice, idProduct)
My query has to select all the products not selled in the year 2019. I can use a function to obtain the year from the date, for example year(i.date). I know that the products that don't appear in the Item-invoice table are the not selled products. So I have tried with this two codes and obtain a good output.
SELECT p.name
FROM Products p
EXECPT
SELECT ii.idProduct
FROM Item-invoices ii, Invoices i
WHERE ii.typeinvoice=i.typeinvoice
AND ii.numberinvoice=i.numberinvocice
AND year(i.date)=2019
And the other code use a sub-query:
SELECT p.name
FROM Products p
WHERE p.idProduct NOT IN
(SELECT ii.idProduct
FROM Item-invoices ii, Invoices i
WHERE ii.typeinvoice=i.typeinvoice
AND ii.numberinvoice=i.numberinvocice
AND year(i.date)=2019)
The answer is how can i use the left join command to have the same output. I've tried with
SELECT p.name
FROM Products p
LEFT JOIN Item-invoices ii ON
p.IdProduct=ii.idProduct
LEFT JOIN Invoices i ON
ii.typeinvoice=i.typeinvoice
AND ii.numberinvoice=i.numberinvocice
WHERE year(i.date)=2019
AND ii.idProduct IS NULL
I know this is wrong but can't find the solution
Any help?
You are almost there. You just need to move the condition on the invoice date to from the from clause to the on clause of the join.
Conditions in the WHERE clause are mandatory, so what you did actually turned the LEFT JOI to an INNER JOIN, which can never be fulfilled (since both conditions in the WHERE clause cannot be true at the same time).
SELECT p.name
FROM Products p
LEFT JOIN Item-invoices ii ON
p.IdProduct=ii.idProduct
LEFT JOIN Invoices i ON
ii.typeinvoice=i.typeinvoice
AND ii.numberinvoice=i.numberinvocice
AND i.date >= '2019-01-01'
AND i.date < '2020-01-01'
WHERE ii.idProduct IS NULL
Note that I changed your date filter to a half-open filter that operates directly on the stored date, without using date functions; this is a more efficient way to proceed (since it allows the database to use an existing index).

2 Tables - one customer, one transactions. How to handle a customer with no transaction?

I have 2 tables-one customers, one transactions. One customer does not have any transactions. How do I handle that? As I'm trying to join my tables, the customer with no transaction does not show up as shown in code below.
SELECT Orders.Customer_Id, Customers.AcctOpenDate, Customers.CustomerFirstName, Customers.CustomerLastName, Orders.TxnDate, Orders.Amount
FROM Orders
INNER JOIN Customers ON Orders.Customer_Id=Customers.Customer_Id;
I need to be able to account for the customer with no transaction such as querying for least transaction amount.
Use below updated query - Right Outer join is used instead of Inner join to show all customers regardless of the customer placed an order yet.
SELECT Orders.Customer_Id, Customers.AcctOpenDate,
Customers.CustomerFirstName, Customers.CustomerLastName,
Orders.TxnDate, Orders.Amount
FROM Orders
Right Outer JOIN Customers ON Orders.Customer_Id=Customers.Customer_Id;
INNER Joins show only those records that are present in BOTH tables
OUTER joins gets SQL to list all the records present in the designated table and shows NULLs for the fields in the other table that are not present
LEFT OUTER JOIN (the first table)
RIGHT OUTER JOIN (the second table)
FULL OUTER JOIN (all records for both tables)
Get up to speed on the join types and how to handle NULLS and that is 90% of writing SQL script.
Below is the same query with a left join and using ISNULL to turn the amount column into 0 if it has no records present
SELECT Orders.Customer_Id, Customers.AcctOpenDate, Customers.CustomerFirstName, Customers.CustomerLastName
, Orders.TxnDate, ISNULL(Orders.Amount,0)
FROM Customers
LEFT OUTER JOIN Orders ON Orders.Customer_Id=Customers.Customer_Id;
try this :
SELECT Orders.Customer_Id, Customers.AcctOpenDate, Customers.CustomerFirstName, Customers.CustomerLastName, Orders.TxnDate, Orders.Amount
FROM Orders
Right OUTER JOIN Customers ON Orders.Customer_Id=Customers.Customer_Id;
I strongly recommend LEFT JOIN. This keeps all rows in the first table, along with matching columns in the second. If there are no matching rows, these columns are NULL:
SELECT c.Customer_Id, c.AcctOpenDate, c.CustomerFirstName, c.CustomerLastName,
o.TxnDate, o.Amount
FROM Customers c LEFT JOIN
Orders o
ON o.Customer_Id = c.Customer_Id;
Although you could use RIGHT JOIN, I never use RIGHT JOINs, because I find them much harder to follow. The logic of "keep all rows in the first table I read" is relatively simple. The logic of "I don't know which rows I'm keeping until I read the last table" is harder to follow.
Also note that I included table aliases and change the CustomerId to come from customers -- the table where you are keeping all rows.
Using CASE will replace "null" with 0 then you can sum the values. This will count customers with no transactions.
SELECT c.Name,
SUM(CASE WHEN t.ID IS NULL THEN 0 ELSE 1 END) as TransactionsPerCustomer
FROM Customers c
LEFT JOIN Transactions t
ON c.Name = t.customerID
group by c.Name
SELECT c.Name,
SUM(CASE WHEN t.ID IS NULL THEN 0 ELSE 1 END) as numberoftransaction
FROM customers c
LEFT JOIN transactions t
ON c.Name = t.customerID
group by c.Name

LEFT JOIN help in sql

I have to make a list of customer who do not have any invoice but have paid an invoice … maybe twice.
But with my code (stated below) it contains everything from the left join. However I only need the lines highlighted with green.
How should I make a table with only the 2 highlights?
Select paymentsfrombank.invoicenumber,paymentsfrombank.customer,paymentsfrombank.value
FROM paymentsfrombank
LEFT OUTER JOIN debtors
ON debtors.value = paymentsfrombank.value
You only want to select columns from paymentsfrombank. So why do you even join?
select invoice_number, customer, value from paymentsfrombank
except
select invoice_number, customer, value from debtors;
(This requires exact matches as in your example, i.e. same amount for the invoice/customer).
There are two issues in your SQL. First, you need to join on Invoice number, not on value, as joining on value is pointless. Second, you need to only pick those payments where there are no corresponding debts, i.e. when you left-join, the table on the right has "null" in the joining column. The SQL would be something like this:
SELECT paymentsfrombank.invoicenumber,paymentsfrombank.customer,paymentsfrombank.value
FROM paymentsfrombank
LEFT OUTER JOIN debtors
ON debtors.InvoiceNumber = paymentsfrombank.InvoiceNumber
WHERE debtors.InvoiceNumber is NULL
in mysql we usually have this way to flip the relation and extract the rows that dosen't have relation.
Select paymentsfrombank.invoicenumber,paymentsfrombank.customer,paymentsfrombank.value
FROM paymentsfrombank
LEFT OUTER JOIN debtors
ON debtors.value = paymentsfrombank.value where debtors.value is null
You can use NOT EXISTS :
SELECT p.*
FROM paymentsfrombank p
WHERE NOT EXISTS (SELECT 1 FROM debtors d WHERE d.invoice_number = p.invoice_number);
However, the LEFT OUTER JOIN would also work if you add filtered with WHERE Clause to filtered out only missing customers that haven't any invoice information :
SELECT p.invoicenumber, p.customer, p.value
FROM paymentsfrombank P LEFT OUTER JOIN
debtors d
ON d.InvoiceNumber = p.InvoiceNumber
WHERE d.InvoiceNumber IS NULL;
Note : I have used table alias (p & d) that makes query to easier read & write.

SQL Server - Calculated column Sum from different table

I have two tables (Items and Item_Types), and I want to do something like the following as a formula in a calculated column in the Item_Types table:
SELECT SUM(Items.Qty_In_Stock) FROM Items WHERE Items.Item_Type = Item_Types.ID
But Management Studio isn't liking it.
How can I do this? Or have I just got my syntax wrong? I'm not great with SQL.
Thanks
EDIT: I think I need to be more specific.
One ItemType can have many Items.
Each Item has a Qty_In_Stock field.
I want to get a sum of all Qty_In_Stock fields where Items.Item_Type = Item_Types.ID - not just a sum of the entire column.
Cheers
Assuming you are trying to show the available qty for each item type:
I would recommend creating a view that shows this information:
You will need to "GROUP BY" the item type.
CREATE VIEW Item_Types_Qty_In_Stock
AS
SELECT it.ID, SUM(i.Qty_In_Stock) AS QtyInStock FROM Items i
INNER JOIN Item_Types it ON i.Item_Type = it.ID
GROUP BY it.ID
GO
Once you have created the view, you can query it as if it were a table:
SELECT * FROM Item_Types_Qty_In_Stock
There is no join for the Item_types table. Put a join and you should be OK.
SELECT SUM(Items.Qty_In_Stock) from Items INNER JOIN Item_Types ON Items.Item_Type = Item_Types.ID GROUP BY Items.Item_Type
Below will return the sum of Qty_In_Stock for all items with at least one Item_type record.
SELECT
SUM(i.Qty_In_Stock)
FROM Items i
INNER JOIN Item_types it on i.Item_Type = it.ID
You may want to use a left join if every item does not have at least one Item_type record Example:
SELECT
SUM(i.Qty_In_Stock)
FROM Items i
LEFT JOIN Item_types it on i.Item_Type = it.ID
Edited
Perhaps this is what you want. This will return an individual sum of Items(qty_in_stock) for each Item_type ID
SELECT
it.Id,
SUM(i.Qty_In_Stock)
FROM Items i
INNER JOIN Item_types it on i.Item_Type = it.ID
GROUP BY it.Id
A computed column is computed from an expression that can use other columns in the same table. The expression can be a noncomputed column name, constant, function, and any combination of these connected by one or more operators. The expression cannot be a subquery.