SQL - List all customers that we did not make a sale to in the year 1996 - sql

This is my functioning SQL query to return the customers that we sold to in 1996:
SELECT C.CustomerID, C.CompanyName
FROM Customers C, Orders O
WHERE C.CustomerID = O.CustomerID AND YEAR(O.OrderDate) = 1996
GROUP BY C.CustomerID, C.CompanyName
ORDER BY C.CustomerID
Now I'm trying to show the opposite; return all customers that we did not sell to in 1996 (even if we did sell to them in other years). This is what I have, however it returns both the customers we didn't sell to in 1996 but also the ones we did:
SELECT C.CustomerID, C.CompanyName FROM Orders O JOIN Customers C
ON O.CustomerID = C.CustomerID
WHERE YEAR(O.OrderDate) != 1996
GROUP BY C.CustomerID, C.CompanyName
ORDER BY C.CustomerID

You can use a correlated subquery that gets the orders from 1996 of a customer with NOT EXISTS.
SELECT c.customerid,
c.companyname
FROM customers c
WHERE NOT EXISTS (SELECT *
FROM orders o
WHERE o.customerid = c.customerid
AND o.orderdate >= '1996-01-01'
AND o.orderdate < '1997-01-01');
Note that you better shouldn't use year() on orderdate as this can prevent indexes form being used, so slowing down the query.

We can build on your existing query and use the left join antipattern:
SELECT C.CustomerID, C.CompanyName
FROM Customers C
LEFT JOIN Orders O
ON C.CustomerID = O.CustomerID
AND O.OrderDate >= '1996-01-01'
AND O.OrderDate < '1997-01-01'
WHERE O.CustomerID IS NULL
ORDER BY C.CustomerID
This phrases as : try to join each customers with the orders they have placed in 1996, and filter on those without any order.
Side note:
always use explicit, standard join (with the ON keyword); old-school, implicit joins should be avoided (no comma in the FROM clause)
as also commented by sticky bit (whose answer is valid and I upvoted it), using date comparison is better form performance than relying on date functions

try this
SELECT C.CustomerID, C.CompanyName FROM Customers C
WHERE
not exists(select 1 FROM Orders O where O.CustomerID = C.CustomerID and YEAR(O.OrderDate) = 1996)
ORDER BY C.CustomerID

Related

SQL - How can you use WHERE instead of LEFT/RIGHT JOIN?

since I am a bit rusty, I was practicing SQL on this link and was trying to replace the LEFT JOIN completly with WHERE. How can i do this so it does the same thing as the premade function in the website?
What I tried so far is:
SELECT Customers.CustomerName, Orders.OrderID
FROM Customers, Orders
WHERE Customers.CustomerID = Orders.CustomerID OR Customers.CustomerID != Orders.CustomerID
Order by Customers.CustomerName;
Thanks in advance for your help.
You are trying to replace
SELECT Customers.CustomerName, Orders.OrderID
FROM Customers
LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID
with
SELECT Customers.CustomerName, Orders.OrderID
FROM Customers, Orders
WHERE ???
this is doomed to failure. Consider Customers has two rows and Orders has zero. The outer join will return two rows.
The cross join (FROM Customers, Orders) will return zero rows.
In standard SQL a WHERE clause can only reduce the rows from that - not increase them so there is nothing you can put for ??? that will give your desired results.
Before ANSI-92 joins were introduced some systems used to have proprietary operators for this, such as *= in SQL Server but this was removed from the product.
This may work for you.
SELECT
c.CustomerName,
o.OrderID
FROM Customers c
LEFT JOIN Orders o
on c.CustomerID = o.CustomerID
Order by c.CustomerName;
If you are trying to replace this:
SELECT c.CustomerName, o.OrderID
FROM Customers c LEFT JOIN
Orders o
ON c.CustomerID = o.CustomerID
ORDER BY c.CustomerName;
Then you can use UNION ALL:
SELECT c.CustomerName, o.OrderID
FROM Customers c JOIN
Orders o
ON c.CustomerID = o.CustomerID
UNION ALL
SELECT c.CustomerName, o.OrderID
FROM Customers c
WHERE NOT EXIST (SELECT 1 FROM Orders o WHERE c.CustomerID = o.CustomerID)
ORDER BY CustomerName
However, the LEFT JOIN is really a much better way to go.

Why this LEFT OUTER JOIN is not including all the Primary Keys from the Left

The customers table has total 1000 customers of which 1500 placed orders in FY 2016. But we want to display all the customers with their total number of orders in FY 2016 whether a customer placed an order in that FY or not. But the following query in SQL Server 2012 is displaying only 1490.
What we may be missing here?
SELECT c.CustomerID, count(*) AS TotalOrders
FROM Customers c
LEFT JOIN Orders o ON c.CustomerID = o.CustomerID
WHERE o.FiscalYear = '2016'
GROUP BY c.CustomerID
UPDATE:
The following query returns only 1 more record (1491) - still missing 9 more records.
SELECT c.CustomerID, count(*) AS TotalOrders
FROM Customers c
LEFT JOIN Orders o ON c.CustomerID = o.CustomerID
AND o.FiscalYear = '2016'
GROUP BY c.CustomerID
You where clause is turning the left outer join into an Inner join.
Change it to AND:
SELECT c.CustomerID, count(o.CustomerID) AS TotalOrders
FROM Customers c
LEFT JOIN Orders o
ON c.CustomerID = o.CustomerID
AND o.FiscalYear = '2016' -- Here
GROUP BY c.CustomerID
The correct SQL is:
SELECT c.CustomerID, count(o.CustomerID) AS TotalOrders,
sum(count(o.CustomerID)) over () as TotalTotalOrders
FROM Customers c LEFT JOIN
Orders o
ON c.CustomerID = o.CustomerID AND o.FiscalYear = '2016'
GROUP BY c.CustomerID;
TotalTotalOrders should be all the orders (or at least the ones with valid customer ids).
This will list all customers, whether or not they have any orders regardless of the year in which the order was placed. The sum will then count all orders that were placed in 2016, ignore the rest, and return an intenger (i.e. it will never be null).
SELECT
c.CustomerID
,sum(case when o.FiscalYear = '2016' then 1 else 0 end) AS TotalOrders
FROM Customers c
LEFT JOIN Orders o
ON c.CustomerID = o.CustomerID
GROUP BY c.CustomerID

SQL COUNT function for results "greater than or equal to"

I currently have a query where it returns the total number of accounts each customer holds, but how do I make it so that it returns only customers who has more than 1 account?
SELECT C.customerID, COUNT(O.accNumber) AS "total"
FROM Customer C, Owns O
WHERE C.customerID = O.customerID
GROUP BY C.customerID
The answer to your question is HAVING. However, you need to learn to use properJOIN syntax. Simple rule: Never use a comma in the FROM clause. Always use explicit JOIN syntax.
SELECT C.customerID, COUNT(O.accNumber) AS total
FROM Customer C JOIN
Owns O
ON C.customerID = O.customerID
GROUP BY C.customerID
HAVING COUNT(*) > 1;
Actually, you don't even need the JOIN:
SELECT o.customerID, COUNT(o.accNumber) AS total
FROM Owns o
GROUP BY o.customerID
HAVING COUNT(*) > 1;
That's much simpler.
Add a HAVING clause
SELECT C.customerID, COUNT(O.accNumber) AS "total"
FROM Customer C, Owns O
WHERE C.customerID = O.customerID
GROUP BY C.customerID
HAVING COUNT(*) > 1
Please try
WHERE C.customerID = O.customerID AND count(O.accNumber) > 1

Left Join in Oracle SQL

I was going through an example of LEFT JOIN on w3schools.com.
http://www.w3schools.com/sql/sql_join_left.asp
SELECT Customers.CustomerName, Orders.OrderID
FROM Customers
LEFT JOIN Orders
ON Customers.CustomerID=Orders.CustomerID
ORDER BY Customers.CustomerName;
The above query will return me all customers with No Orders as NULL Order ID+ All customers having Orders with their Order Ids
How should I modify this query so that it returns All Customers with No Orders + All Customers having Orders with Order date as '1996-09-18'
Thanks in advance.
If you want customers with no orders and those with a specific order date, then you want a WHERE clause:
SELECT c.CustomerName, o.OrderID
FROM Customers c LEFT JOIN
Orders o
ON c.CustomerID = o.CustomerID
WHERE (o.CustomerID is NULL) OR (o.OrderDate = DATE '1996-09-18)
ORDER BY c.CustomerName;
If you wanted all customers with their order on that date (if they have one), then you would move the condition to the ON clause:
SELECT c.CustomerName, o.OrderID
FROM Customers c LEFT JOIN
Orders o
ON c.CustomerID = o.CustomerID AND o.OrderDate = DATE '1996-09-18
ORDER BY c.CustomerName;
Note the difference: the first filters the customers. The second only affects what order gets shown (and NULL will often be shown).

How do you select an exact date in SQL?

select o.customerid, c.customername, o.orderdate
from orders as o, customers as c
where o.orderdate='1997-08-26';
Using the sample northwind db, I can't quite figure out what is wrong? I have used the format of the date that is used in the sample table.
I am trying to extract the ID and name of anyone that placed an order on the 26th.
You need to JOIN the orders and customers tables to each other:
select o.customerid, c.customername, o.orderdate
from orders as o, customers as c
where o.orderdate='19970826'
AND o.customerid = c.customerid
Using explicit syntax:
SELECT o.customerid,
c.customername,
o.orderdate
FROM orders AS o
JOIN customers c
ON c.customerid = o.customerid
WHERE o.orderdate = '19970826'
You should also read about explicit vs. implicit JOIN syntax.
This method will allow the query to use an index on orderdate:
SELECT o.customerid,
c.customername,
o.orderdate
FROM orders AS o
JOIN customers c
ON c.customerid = o.customerid
WHERE o.orderdate >= '1997-08-26'
AND o.orderdate < '1997-08-27'
try this
select o.customerid, c.customername, o.orderdate
from orders as o inner join customers as c
on o.customerid=c.customerid
where CONVERT(VARCHAR(10), o.orderdate, 120)='1997-08-26';
Updated with Join