Joining 3 tables on sql and checking conditions - mysql

SelectClassicModels.Orders.CustomerNumber,
ClassicModels.CUSTOMERS.CUSTOMERNAME,
ClassicModels.Employees.LASTNAME,
ClassicModels.Employees.firstNAME,
ClassicModels.employees.EmployeeNumber
from ClassicModels.Orders
join
ClassicModels.Customers
on ClassicModels.Orders.CustomerNumber = ClassicModels.Customers.CUSTOMERNUMBER
join
ClassicModels.EMPLOYEES
on ClassicModels.Employees.EMPLOYEENUMBER = ClassicModels.CUSTOMERS.SalesRepEmployeeNumber
Those three tables when joined work just fine, but when I try and add these modifiers, they don't work
group by ClassicModels.Orders.CustomerNumber
having count(ClassicModels.Orders.CustomerNumber) < 4

First off, welcome to StackOverflow!
I've re-formatted your SQL using my favorite tool, which I can provide a link to if you're interested. I've also added aliases to help make it more "readable". (The aliases are the lowercase bits after the table name in the FROM and JOIN clauses.)
SELECT orders.CustomerNumber,
customers.CustomerName,
employees.LastName,
employees.FirstName,
employees.EmployeeNumber
FROM CLASSICMODELS.ORDERS orders
JOIN CLASSICMODELS.CUSTOMERS customers
ON orders.CustomerNumber = customers.CustomerNumber
JOIN CLASSICMODELS.EMPLOYEES employees
ON employees.EmployeeNumber = customers.SalesRepEmployeeNumber
Now that we've got that done. Let's add your GROUP BY and HAVING clauses.
GROUP BY clauses have to have ALL of the columns used in the SELECT clause in them. (I'm not sure why. I haven't looked it up, but I just know that's how it works. :) )
SELECT orders.CustomerNumber,
customers.CustomerName,
employees.LastName,
employees.FirstName,
employees.EmployeeNumber
FROM CLASSICMODELS.ORDERS orders
JOIN CLASSICMODELS.CUSTOMERS customers
ON orders.CustomerNumber = customers.CustomerNumber
JOIN CLASSICMODELS.EMPLOYEES employees
ON employees.EmployeeNumber = customers.SalesRepEmployeeNumber
GROUP BY orders.CustomerNumber,
customers.CustomerName,
employees.LastName,
employees.FirstName,
employees.EmployeeNumber
Now that should work. Then you just have to add your HAVING clause in there.
SELECT orders.CustomerNumber,
customers.CustomerName,
employees.LastName,
employees.FirstName,
employees.EmployeeNumber
FROM CLASSICMODELS.ORDERS orders
JOIN CLASSICMODELS.CUSTOMERS customers
ON orders.CustomerNumber = customers.CustomerNumber
JOIN CLASSICMODELS.EMPLOYEES employees
ON employees.EmployeeNumber = customers.SalesRepEmployeeNumber
GROUP BY orders.CustomerNumber,
customers.CustomerName,
employees.LastName,
employees.FirstName,
employees.EmployeeNumber
HAVING COUNT(orders.CustomerNumber) < 4
I was also looking over your query, and you might get faster (and more efficient results) by using a query like this:
WITH CUSTOMERSWITHLESSTHANFOURORDERS
AS
(
SELECT CUSTOMERNUMBER
FROM CLASSICMODELS.ORDERS
GROUP BY CUSTOMERNUMBER
HAVING COUNT(CUSTOMERNUMBER) < 4
)
SELECT O.CUSTOMERNUMBER,
C.CUSTOMERNAME,
E.LASTNAME,
E.FIRSTNAME,
E.EMPLOYEENUMBER
FROM CUSTOMERSWITHLESSTHANFOURORDERS O
JOIN CLASSICMODELS.CUSTOMERS C
ON O.CUSTOMERNUMBER = C.CUSTOMERNUMBER
JOIN CLASSICMODELS.EMPLOYEES E
ON E.EMPLOYEENUMBER = C.SALESREPEMPLOYEENUMBER;
It uses what is called "common table expression" and basically just isolates a portion of the query. It could be more efficient because it's going to try and group on less data, so it could be faster. Be wary, because there's lots of "could"'s in there because I don't know how various different things are set up in your MySQL database.
Good luck!

Related

using MYSQL Group By to get the most popular value

I am practicing MYSQL using https://www.w3schools.com/mysql/trymysql.asp?filename=trysql_func_mysql_concat which has a mock database for me to practice with an I am experimenting using the GROUP BY command I am attempting to group all employees up with all of their sales and determine, their name, their amount of sales and the product that they sold the most. I have managed to get their name and sales but not the product name. I know that extracting information with a group by is difficult and I have tried using a sub query. Is there a way to get the information.
My query is below.
SELECT
CONCAT_WS(' ',
Employees.FirstName,
Employees.LastName) AS 'Employee name',
COUNT(*) AS 'Num of sales'
FROM
Orders
INNER JOIN
Employees ON Orders.EmployeeID = Employees.EmployeeID
INNER JOIN
OrderDetails ON OrderDetails.OrderID = Orders.OrderID
INNER JOIN
Products ON Products.ProductID = OrderDetails.ProductID
GROUP BY Orders.EmployeeID
ORDER BY COUNT(*) DESC;
What this says is get orders, join employees based on orders employeeid, join the order details based on order id and join products information based on product id in the order details, then it groups them by the employee id and orders them by the number of sales an employee has made.
SELECT
concat_ws(' ',
Employees.FirstName,
Employees.LastName) as 'Employee name',
count(*) as 'Num of sales',
(
SELECT Products.ProductName
FROM Orders
INNER JOIN Employees ON Orders.EmployeeID = Employees.EmployeeID
INNER JOIN OrderDetails ON OrderDetails.OrderID = Orders.OrderID
INNER JOIN Products ON Products.ProductID = OrderDetails.ProductID
GROUP BY Orders.EmployeeID
ORDER BY count(Products.ProductName) desc
LIMIT 1
) as 'Product Name'
FROM Orders
INNER JOIN Employees ON Orders.EmployeeID = Employees.EmployeeID
INNER JOIN OrderDetails ON OrderDetails.OrderID = Orders.OrderID
INNER JOIN Products ON Products.ProductID = OrderDetails.ProductID
GROUP BY Orders.EmployeeID
ORDER BY count(*) desc;
Above is my attempt at using a sub query for the solution.
It is quite ugly, as the w3school uses still mysql 5.7
On a personal note, you should install your own server grab somewhere a database and test it there, in mysql workbench you can have many query tabs in which you can test queries , till you het the "right" result.
SELECT
CONCAT_WS(' ',
Employees.FirstName,
Employees.LastName) AS 'Employee name',
COUNT(*) AS 'Num of sales',
tn.ProductName
FROM
Orders
INNER JOIN
Employees ON Orders.EmployeeID = Employees.EmployeeID
INNER JOIN
OrderDetails ON OrderDetails.OrderID = Orders.OrderID
INNER JOIN
Products ON Products.ProductID = OrderDetails.ProductID
INNEr JOIN
(SELECT EmployeeID, p.ProductName
FROM (SELECT IF (#Eid = EmployeeID ,#rn := #rn +1, #rn := 1) rn,ProductID, sumamount
, #Eid := EmployeeID as EmployeeID
FROM
(
SELECT
EmployeeID,ProductID, SUM(Quantity) sumamount
FROM Orders o INNER JOIN OrderDetails od ON od.OrderID = o.OrderID,(SELECT #Eid := 0, #rn := 0) t1
GROUP BY EmployeeID,ProductID
ORDER BY EmployeeID,sumamount DESC ) t2 ) t3
INNER JOIN Products p ON t3.ProductID = p.ProductID
WHERE rn= 1) tn
ON Orders.EmployeeID = tn.EmployeeID
GROUP BY Orders.EmployeeID
ORDER BY COUNT(*) DESC;
In your second query you are trying to get an employee's most often sold product. But there are two mistakes in that subquery:
The subquery is invalid. You group by employee, but select a product. Which product? An employee can sell many different products. MySQL should raise a syntax error here, as all other DBMS I know of do. But you are in cheat mode. MySQL allows incorrect aggregation queries and silently applies ANY_VALUE on all columns that cannot be selected otherwise. Thus you are selecting ANY_VALUE(Products.ProductName), i.e. a product arbitrarily chosen by the DBMS. To get out of cheat mode SET sql_mode = 'ONLY_FULL_GROUP_BY';.
Then, you don't relate the subquery to your main query. So when selecting the row for, say, employee #123, your subquery still selects data for all employees in order to pick one of their products. And as this is independent from the employee in the main query, it will probably pick the same product for every other employee you are selecting, too.
Here is what the query should look like instead:
SELECT
concat_ws(' ', e.FirstName, e.LastName) as "Employee name",
count(*) as "Num of sales",
(
SELECT p2.ProductName
FROM Orders o2
INNER JOIN OrderDetails od2 ON od2.OrderID = o2.OrderID
INNER JOIN Products p2 ON p2.ProductID = od2.ProductID
WHERE o2.EmployeeID = o.EmployeeID
GROUP BY p2.ProductID
ORDER BY count(*) DESC
LIMIT 1
) as "Product Name"
FROM Orders o
INNER JOIN Employees e ON o.EmployeeID = e.EmployeeID
INNER JOIN OrderDetails od ON od.OrderID = o.OrderID
GROUP BY o.EmployeeID
ORDER BY count(*) desc;
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=f35e96764d454a4032d7778b550fc6b4
Disclaimer: When an employee sold more than one product most often (e.g. 500 x product A, 500 x product B, 200 x product C), then one of them (A or B in the example) gets picked arbitrarily for the employee.

MySQL JOIN working properly only when selecting every column with wildcard

This was probably asked a few times, but I couldn't find an answer. While doing JOIN on two tables, I encounter a problem.
First table is named "Employees" and another "Orders".
I'm trying to create a query, which will give me the output like:
order_id (from Orders) | first_name (from Employees) | last_name (from Employees)
The queries I use are:
SELECT * FROM Orders
LEFT JOIN Employees e on Orders.employeeid = e.EmployeeID;
or full join:
SELECT * FROM Orders
LEFT JOIN Employees e on Orders.employeeid = e.EmployeeID
UNION
SELECT * FROM Employees
RIGHT JOIN Orders o on Employees.EmployeeID = o.employeeid;
both work just fine, giving me the same results. Unless I select which columns I wish to extract. So query like that:
SELECT Orders.orderid, e.first_name, e.last_name FROM orders
LEFT JOIN Employees e on orders.employeeid = e.EmployeeID;
Gives me totally different results. Eg. first 100 orderids have same employee name, then another 100 different one and so on (only 4 employees overall, should be 9).
What am I doing wrong?
EDIT (screenshots added):
Orders table:
Employees table:
Output when doing full join, everything seems to be ok:
Left join (or any other join, looks the same). Some orders seem to be ommited, but overall only 4 employees are listed.
I don't know your data, but I think you want:
SELECT o.orderid, e.first_name, e.last_name FROM orders o INNER JOIN Employees e ON o.employeeid = e.employeeId
where o.employeeid != null
Order by o.orderid
It's hard to tell without seeing some data samples.
But here are a few points:
With LEFT JOIN you get 'only 4 employees overall, should be 9' -- I assume there are 5 employees that do not have any order.
In the 'full join' you get mixed columns, the correct way should be
SELECT * FROM Orders
LEFT JOIN Employees e on Orders.employeeid = e.EmployeeID
UNION
SELECT * FROM Orders
RIGHT JOIN Employees e on Orders.employeeid = e.EmployeeID
Otherwise there should be no difference in the output if you specify the columns, as opposed to *.
If
SELECT * FROM Orders
LEFT JOIN Employees e on Orders.employeeid = e.EmployeeID
is returning the correct set of results, then this should probably work:
Select orderid, FirstName, LastName from (SELECT * FROM Orders
LEFT JOIN Employees e on Orders.employeeid = e.EmployeeID LIMIT 999999) as joinedTable

Need to create a query that will show orders for April Roberts. I just need output to show OrderID and OrderDate. Here is what I have so far

SELECT
ORDER_ITEM.OrderID,
ORDERS.OrderDate
FROM ORDER_ITEM LEFT JOIN ORDERS
ON ORDER_ITEM.OrderID = ORDERS.OrderID
LEFT JOIN PEOPLE
ON ORDERS.CustomerID = PEOPLE.PeopleId AND ORDERS.EmployeeID = PEOPLE.PeopleId
where PEOPLE.FirstName + PEOPLE.LastName = 'April Roberts'
My results are outputting 0 rows. Not sure what I am doing wrong here.
Make sure you have data in the table that match your criteria. e.g. You have orders by the person with the name April Roberts. If you use LEFT JOIN, the query will return all results despite if you do not have any order by the person. So, INNER JOIN or simply JOIN is fine if you ONLY want orders that associates with the person.
SELECT oi.OrderID, o.OrderDate
FROM ORDER_ITEM oi
JOIN ORDERS o ON oi.OrderID = o.OrderID
JOIN PEOPLE p ON o.CustomerID = p.PeopleId
WHERE p.FirstName = 'April' AND p.LastName = 'Roberts'
This should work better for the search (you are using a space that doesn't exist)
SELECT OI.OrderID, O.OrderDate
FROM ORDER_ITEM OI
LEFT JOIN ORDERS O ON OI.OrderID = O.OrderID
LEFT JOIN PEOPLE P ON O.CustomerID = P.PeopleId AND O.EmployeeID = P.PeopleId
WHERE P.FirstName='April' AND P.LastName = 'Roberts';
However, logically, CustomerID and EmployeeID can't match the same employee. You'd need a second link to people
SELECT OI.OrderID, O.OrderDate
FROM ORDER_ITEM OI
LEFT JOIN ORDERS O ON OI.OrderID = O.OrderID
LEFT JOIN PEOPLE P ON O.CustomerID = P.PeopleId
LEFT JOIN PEOPLE E ON O.EmployeeID = E.PeopleId
WHERE P.FirstName='April' AND P.LastName = 'Roberts';
Your error is that you want the people record match employee and customer of the same order, but the customer will never equal the employee of course.
Is April Roberts the customer or the employee? Decide and then select the data from the orders table for that customer or employee:
select orderid, orderdate
from orders
where customerid in
(
select peopleid
from people
where firstname = 'April'
and lastname = 'Roberts'
);
No need to join the order_item table. No need to join at all. Only join when you want joined data.
I am using IN for the case there exist two April Roberts. If there is a constraint on the table preventing that, then you can change IN to =.
If you were really interested in the orders linked to some April Roberts, not knowing whether she is employee or customer, you'd use EXISTS instead:
select orderid, orderdate
from orders o
where exists
(
select *
from people p
where p.firstname = 'April'
and p.lastname = 'Roberts'
and p.peopleid in (o.customerid, o.employeeid)
);
There is another error in your query by the way:
where PEOPLE.FirstName + PEOPLE.LastName = 'April Roberts'
You are adding two values here (+ is for numeric addition), so MySQL converts the strings FirstName and LastName to numbers. Such conversion from a string that starts with a letter results in the value zero in MySQL, so you have where 0 + 0 = 0 which is always true. If + would do string concatenation as you obviously supposed (which is the || operator in standard SQL and the CONCAT function in MySQL), then you'd probably have gotten where 'AprilRoberts' = 'April Roberts' which would always be false.

Inner join usage with array

I try to make a filter for my project. And I want to learn how can I use array values in inner join. For example I have a query like this.
SELECT Orders.OrderID, Employees.LastName, Employees.FirstName
FROM Orders
INNER JOIN Employees
ON Orders.EmployeeID = Employees.EmployeeID;
But I have multiple EmployeeIDs in array and I try to make my query like this.
SELECT Orders.OrderID, Employees.LastName, Employees.FirstName
FROM Orders
INNER JOIN Employees
ON Orders.EmployeeID = Employees.1,2,3;
this is the wrong way I know but I really don't know the right way to make this query.
No need to worry about that if there are any number of rows of employee id mapped with the orders it will return that many rows if exist, rather just use a where in clause
SELECT Orders.OrderID, Employees.LastName, Employees.FirstName
FROM Orders
INNER JOIN Employees ON Orders.EmployeeID = Employees.EmployeeID
WHERE Employees.EmployeeID IN (1,2,3);`
You can do like this,
SELECT Orders.OrderID, Employees.LastName, Employees.FirstName
FROM Orders
INNER JOIN Employees
ON Orders.EmployeeID = Employees.EmployeeID where Employees.EmployeeID IN (1,2,3);
Inner join with employee id will fetch all matching records of both orders and employees with condition of employee id and then on that where clause to fetch those records whose employee id is 1,2,3.
You could use the in operator in the on clause:
SELECT Orders.OrderID, Employees.LastName, Employees.FirstName
FROM Orders
INNER JOIN Employees
ON Orders.EmployeeID = Employees.EmployeeID and Employees.EmployeeID in (1,2,3);

MySQL - What's wrong with the WHERE clause in my JOIN query?

I managed to cobble together a join statement to give me a single table that displays my entire database. Now I need to narrow the results down to just the last 30 days, but I cannot for the life of me figure out how to work this restriction into the query.
Right now I have:
select customers.name, customers.email, orderinfo.orderno, orderinfo.orderdate, status.state, orderinfo.lastupdate
from customers
where orderinfo.orderdate = curdate()
inner join orderinfo on customers.id=orderinfo.fk_id
inner join status on orderinfo.fk_code=status.code
order by customers.name;
...in an attempt to just narrow it down to showing me all the records for today, but even that is not working. I see there are a variety of ways of doing the 30-day thing but the part I'm having the most trouble with is just getting the WHERE clause to work at all in this query.
Thanks for any advice!
The WHERE clause needs to go before the ORDER BYand after all joins.
select customers.name, customers.email, orderinfo.orderno, orderinfo.orderdate, status.state, orderinfo.lastupdate
from customers
inner join orderinfo on customers.id=orderinfo.fk_id
inner join status on orderinfo.fk_code=status.code
where orderinfo.orderdate = curdate()
order by customers.name;
You didn't specify the orderinfo in your FROM part.
select customers.name, customers.email, orderinfo.orderno, orderinfo.orderdate, status.state, orderinfo.lastupdate
from customers
inner join orderinfo on customers.id=orderinfo.fk_id
inner join status on orderinfo.fk_code=status.code
where orderinfo.orderdate = curdate()
order by customers.name;
Your inner joins should come before your where clause
select customers.name, customers.email, orderinfo.orderno, orderinfo.orderdate, status.state, orderinfo.lastupdate
from customers
inner join orderinfo on customers.id=orderinfo.fk_id
inner join status on orderinfo.fk_code=status.code
where orderinfo.orderdate = curdate()
order by customers.name;
If you need to do the query with the condition first, then you need subquery
Select * From
(
select customers.name, customers.email, orderinfo.orderno, orderinfo.orderdate, status.state, orderinfo.lastupdate
from customers
where orderinfo.orderdate = curdate()
) customers
inner join orderinfo on customers.id=orderinfo.fk_id
inner join status on orderinfo.fk_code=status.code
order by customers.name;
SELECT customers.name, customers.email, orderinfo.orderno, orderinfo.orderdate, status.state, orderinfo.lastupdate
FROM customers
INNER JOIN orderinfo ON customers.id=orderinfo.fk_id
INNER JOIN status ON orderinfo.fk_code=status.code
WHERE orderinfo.orderdate = curdate()
ORDER BY customers.name;
WHERE needs to be after all JOINS and before ORDER BY