Select query with and/or in the from/join clause - sql

Say I have SQL Select query like so:
SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDate
FROM Orders
INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID;
is there a way to use AND/OR in the JOIN clause? something like this:
SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDate
FROM Orders
INNER JOIN Customers ON (
Orders.CustomerID = Customers.CustomerID
AND
Orders.CustomerName = Customers.CustomerName
)
I am not even sure if that makes sense - but if this is possible, if someone knows the name of this type of query, let me know.

Yes, you can have multiple join condition.
Below queries are valid
Select a.a1, b.b1 from table1 a inner join table2 b on a.a1=b.a1 and a.b1=b.b1;
You can even have filters, functions as well.
Select a.a1, b.b1 from table1 a inner join table2 b on a.a1=b.a1 and a.b1=b.b1 and b.c1='someValue';
Select a.a1, b.b1 from table1 a inner join table2 b on a.a1=b.a1 and a.b1=b.b1 and <functions>

SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDate
FROM Orders
INNER JOIN Customers ON
Orders.CustomerID = Customers.CustomerID
AND Orders.CustomerName = Customers.CustomerName
Yes it is possible to join on multiple fields.

Yes, you can put any boolean expression in the on clause, even a reference to a subquery. So this is valid:
SELECT o.OrderID, c.CustomerName, o.OrderDate
FROM Orders o INNER JOIN
Customers c
ON o.CustomerID = c.CustomerID AND
o.CustomerName = c.CustomerName;
That said, you have a problem with your data model. You should not be storing the customer name twice -- in both the Orders table and the Customers table. It should be in the Customers table. And, you can look it up using CustomerId.
So, I would recommend fixing your data model so you don't need to use two keys for this JOIN.

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.

Which direction need for INNER JOIN?

If I check this example: https://www.w3schools.com/sql/sql_join_inner.asp
This make sense:
SELECT Orders.OrderID, Customers.CustomerName
FROM Orders
INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID;
but this would work? I do not think so, right?
SELECT Orders.OrderID, Customers.CustomerName
FROM Customers
INNER JOIN Orders ON Orders.CustomerID = Customers.CustomerID;
I should always do child INNER JOIN parent and never parent INNER JOIN child, right?
You can do the inner join in either order. The only difference for an inner join is aesthetic. The ordering doesn't event affect performance (because the database decides on the best execution path).
I do recommend table aliases either way:
SELECT o.OrderID, c.CustomerName
FROM Customers c INNER JOIN
Orders o
ON o.CustomerID = c.CustomerID;

Beginner: LEFT JOIN not doing what it should?

I'm having trouble with a really simple left join statement that's driving me nuts
I wanted to count the numbers of orders from each customer, that's fine, but I want to display the name, and I'm joining with the customers table and trying to select the name and it says that CustomerName is not part of an aggregate function, it's really weird.
SELECT Customers.CustomerName as 'Name',
COUNT(*) AS 'Order Count'
FROM Orders
LEFT JOIN Customers
ON Orders.CustomerID = Customers.CustomerID
GROUP BY Customers.CustomerID
Thanks for any tips.
You need to count the rows from the orders table, and the left join should be in the other direction:
SELECT c.customerid,
c.CustomerName as "Name",
COUNT(o.customerid) AS "Order Count"
FROM Customers c
LEFT JOIN Orders o ON o.CustomerID = cs.CustomerID
GROUP BY c.CustomerID, c.customername;
count() will ignore NULL values that come into the result due to the outer join so it will count the number of orders for each customers. Customers without orders will be show with a zero count.
Include CustomerName in Group BY instead of CustomerID
SELECT Customers.CustomerName as 'Name', COUNT(*) AS 'Order Count'
FROM Orders LEFT JOIN Customers ON Orders.CustomerID = Customers.CustomerID
GROUP BY Customers.CustomerName
If you are using SQL Server then try using OVER() without Group BY
SELECT Customers.CustomerName as 'Name', COUNT(*) OVER (PARTITION BY Customers.CustomerName ORDER BY Customers.CustomerName)AS 'Order Count'
FROM Orders LEFT JOIN Customers ON Orders.CustomerID = Customers.CustomerID
Modify as below. column used in group by clause should be in column queried in select clause
SELECT Customers.CustomerName as 'Name',
COUNT(*) AS 'Order Count'
FROM Orders
LEFT JOIN Customers
ON Orders.CustomerID = Customers.CustomerID
GROUP BY Customers.CustomerName
I have just reordered your query,please try this it will definitely work for you.
SELECT Customers.CustomerName as 'Name',
COUNT(*) AS 'Order Count'
FROM Customers
LEFT JOIN Orders
ON Customers.CustomerID=Orders.CustomerID
GROUP BY Customers.CustomerID
A simple approach to get all the columns in the customers table is to use a correlated subquery:
select c.*, -- or whatever columns you want
(select count(*)
from orders o
where o.CustomerID = c.CustomerID
) as order_count
from customers c;
Because this avoids the outer GROUP BY, this also has the advantage of having better performance in most databases, particularly with an index on orders(CustomerId). Plus, it returns 0 if the customer has no orders. And, it allows you to choose any or all of the columns from Customers.
The correct way to get the counts you want is to count a column from Orders:
SELECT c.CustomerName, c.CustomerID,
COUNT(o.CustomerId) AS Order_Count
FROM Customers c LEFT JOIN
Orders o
ON o.CustomerID = c.CustomerID
GROUP BY c.CustomerID, c.CustomerName;
Notes:
The Customers table goes first in the LEFT JOIN because presumably you want all rows in Customers.
Table aliases make the query easier to write and to read.
Do not use single quotes for column aliases, even if your database supports it. The best method is to choose aliases that do not need to be supported.
Include the CustomerId in the logic, just in case two customers have the same name.
Count a column from Orders so you get a count of 0 for customers with no orders.

Using DISTINCT in SQL Query

How do i use DISTINCT command in a SQL query to show the supplier id, company name, and the number of distinct products from that supplier that were ordered prior to a specific date? I ran the code in Access but it doesn't translate over to SQL efficiently. The table appears.
[Supplier ID Company Name Product Name Order Date
1 Exotic Liquids Chang 17-Aug-94
1 Exotic Liquids Chang 22-Nov-94
1 Exotic Liquids Aniseed Syrup 26-Sep-94]
The code I have so far is the following. Where I get confused is where to put the DISTINCT statement. Should it be immediately after the Select? Should it go in Parentheses in addition to the SELECT? Excuse my lack of knowledge on this subject in advance.
SELECT Suppliers.SupplierID, Customers.CompanyName, Products.ProductName,
Orders.OrderDate
FROM Suppliers INNER JOIN
Products ON Suppliers.SupplierID = Products.SupplierID CROSS JOIN
Customers INNER JOIN
Orders ON Customers.CustomerID = Orders.CustomerID
WHERE Orders.OrderDate <='1/1/1999'
ORDER BY Suppliers.SupplierID
You can either distinct by all columns selected :
SELECT DISTINCT
Suppliers.SupplierID, Customers.CompanyName, Products.ProductName,
Orders.OrderDate
FROM
Suppliers INNER JOIN
Products ON Suppliers.SupplierID = Products.SupplierID CROSS JOIN
Customers INNER JOIN
Orders ON Customers.CustomerID = Orders.CustomerID
WHERE
Orders.OrderDate <='1/1/1999'
ORDER BY
Suppliers.SupplierID
or use group by instead if you need to distinct only by SupplierID. DISTINCT is not a function, hence DISTINCT(Suppliers.SupplierID) means the same as simply put DISTINCT word after SELECT in this case (see the 2nd reference below).
For Reference :
http://blog.sqlauthority.com/2007/12/20/sql-server-distinct-keyword-usage-and-common-discussion/
http://weblogs.sqlteam.com/jeffs/archive/2007/10/12/sql-distinct-group-by.aspx
I'm pretty sure it's this:
SELECT DISTINCT(Suppliers.SupplierID), Customers.CompanyName, Products.ProductName,Orders.OrderDate
FROM Suppliers INNER JOIN
Products ON Suppliers.SupplierID = Products.SupplierID CROSS JOIN
Customers INNER JOIN
Orders ON Customers.CustomerID = Orders.CustomerID
WHERE Orders.OrderDate <='1/1/1999'
ORDER BY Suppliers.SupplierID

SQL Query Showing 4x Records

The following statement works properly but shows each record 4 times. Repeated; I know the relationship is wrong but no idea how to fix it? Apologies if this is simple and i've missed it.
SELECT Customers.First_Name, Customers.Last_Name, Plants.Common_Name, Plants.Flower_Colour, Plants.Flowering_Season, Staff.First_Name, Staff.Last_Name
FROM Customers, Plants, Orders, Staff
INNER JOIN Orders AS t2 ON t2.Order_ID = Staff.Order_ID
WHERE Orders.Order_Date
BETWEEN '2011/01/01'
AND '2013/03/01'
You are generating a Cartesian product between the tables since you have not provided join syntax between any of the tables:
SELECT c.First_Name, c.Last_Name,
p.Common_Name, p.Flower_Colour, p.Flowering_Season,
s.First_Name, s.Last_Name
FROM Customers c
INNER JOIN Orders o
on c.customerId = o.customer_id
INNER JOIN Plants p
on o.plant_id = p.plant_id
INNER JOIN Staff s
ON o.Order_ID = s.Order_ID
WHERE o.Order_Date BETWEEN '2011/01/01' AND '2013/03/01'
Note: I am guessing on column names for the joins
Here is a great visual explanation of joins that can help in learning the correct syntax
In the FROM... clause you are doing a cross join - combining every customer with every plant with every order with every staff.
You should only mention one table in the FROM clause and then connect the other ones with INNER JOINS to only get related records.
I don't know exactly how your database looks like, but something like this:
SELECT Customers.First_Name, Customers.Last_Name, Plants.Common_Name,
Plants.Flower_Colour, Plants.Flowering_Season, Staff.First_Name, Staff.Last_Name
FROM Customers
INNER JOIN Orders ON Orders.Customer_ID = Customers.Customer_ID
INNER JOIN Staff ON Staff.Staff_ID = Orders.Staff_ID
INNER JOIN Plants ON Plants.Plants_ID = Orders.Plants_ID
WHERE Orders.Order_Date
BETWEEN '2011/01/01'
AND '2013/03/01'
This is because you are selecting from four tables without any joins between them, and also because you are joining Orders twice. As the result, a Cartesian product is made.
Here is how you should fix it: re-write the theta join using the ANSI syntax, and provide proper join conditions:
SELECT Customers.First_Name, Customers.Last_Name, Plants.Common_Name, Plants.Flower_Colour, Plants.Flowering_Season, Staff.First_Name, Staff.Last_Name
FROM Customers
JOIN Plants ON ...
JOIN Orders ON ...
JOIN Staff ON ...
INNER JOIN Orders AS t2 ON t2.Order_ID = Staff.Order_ID
WHERE Orders.Order_Date BETWEEN '2011/01/01' AND '2013/03/01'
Replace ... with proper join conditions; this should make the results look as expected.