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
Related
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.
Here is the Query
SELECT o.OrderID, o.CNIC,
(SELECT FullName FROM Customer WHERE CNIC=o.CNIC) Customer,
o.Date
FROM orders o
JOIN ordersproduct op ON op.OrderID=o.OrderID
WHERE o.OrderID=1;
Here is the result
1 - 15604-5566123-2 - Shaiz Mehran - 2020-09-30
1 - 15604-5566123-2 - Shaiz Mehran - 2020-09-30
If the purpose is to only show customers who has an order, but no details of the orderproduct itself you could use the following :
SELECT DISTINCT o.OrderID, o.CNIC,
(SELECT FullName FROM Customer WHERE CNIC=o.CNIC) Customer,
o.Date
FROM orders o
WHERE o.OrderID=1;
OR
SELECT DISTINCT o.OrderID, o.CNIC,
c.Customer, o.Date
FROM orders o
INNER JOIN Customer c ON c.CNIC = o.CNIC
WHERE o.OrderID=1;
If this is only the start and you will show the product which the customer ordered it will show two rows as the customer has two products (orderproducts). This will return two rows with different orderproducts
SELECT DISTINCT o.OrderID, o.CNIC,
c.Customer, o.Date op.ColumnTosShow
FROM orders o
INNER JOIN Customer c ON c.CNIC = o.CNIC
INNER JOIN ordersproduct op ON op.OrderID=o.OrderID
WHERE o.OrderID=1;
SELECT Distinct Orders.OrderID, Orders.CNIC, Customer.FullName
FROM (Orders LEFT JOIN Customer ON Orders.CNIC = Customer.CNIC)
LEFT JOIN OrdersProduct ON Orders.OrderID = OrdersProduct.OrderID;
The reason you are getting multiple rows is because an order can have multiple products . . . and you are getting one row per product.
The join to the product table seems quite unnecessary, so just use:
SELECT o.OrderID, o.CNIC,
(SELECT FullName FROM Customer WHERE CNIC=o.CNIC) as Customer,
o.Date
FROM orders o
WHERE o.OrderID = 1;
I am guessing you have a reasonable data model so there are not multiple rows in orders for a given id. SELECT DISTINCT is not necessary.
I should add that JOIN is very reasonable for this query, but like this:
SELECT o.OrderID, o.CNIC, c.FullName as Customer,
o.Date
FROM orders o LEFT JOIN
customer c
ON o.CNIC = c.CNIC
WHERE o.OrderID = 1;
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.
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);
I have the folowing tables.
ORDER
OrderNumber
CustomerNumber
EmployeeNumber
OrderDate
CUSTOMER
CustomerNumber
Name
Address
EMPLOYEE
EmployeeNumber
Name
Address
ORDERDETAIL
OrderNumber
Qty
Description
Price
Let say ORDERDETAIL table has 10 records
I would like to write a query that will return 10 records from ORDERDETAIL table to include Employee name, employee address, customer name, customer address and and order Date.
I know that I could write a query and use INNER JOIN to get the info from ORDER table, but how do you create the rest of query to get the info from the CUSTOMER and EMPLOYEE tables.
SELECT *
FROM OrderDetail D
INNER JOIN Order O
ON D.OrderNumber = O.OrderNumber;
Just add some more joins...
SELECT *
FROM OrderDetail D
JOIN Order USING (OrderNumber)
JOIN Customer USING (CustomerNumber)
JOIN Employee USING (EmployeeNumber)
You might want to re-order the JOINs in order to have the smallest tables first, as this could provide you with some performance boost (depending on your server's version, the most recent will optimize the join for you and might actually execute the joins in the "probably best" way).
Also, in the MySQL dialect at least, JOIN implicitly expands to INNER JOIN, and writing
A JOIN B USING (COL)
is equivalent to writing
A JOIN B ON (A.COL = B.COL)
SELECT *
FROM OrderDetail D
INNER JOIN Order O ON D.OrderNumber = O.OrderNumber
INNER JOIN Eployee E on O.EployeeNumber = E.EployeeNumber
INNER JOIN Customer C on O.CustomerNumber = C.CustomerNumber
if you have foreign key reference between
ORDER.EmployeeNumber and EMPLOYEE.EmployeeNumber
ORDER.CustomerNumber and CUSTOMER.CustomerNumber
then try this
SELECT
E.name AS employeeName,
E.Address AS employeeAddress,
C.name AS customerName,
C.Address AS customerAddress.
O.OrderDate
FROM OrderDetail D
INNER JOIN Order O ON D.OrderNumber = O.OrderNumber
INNER JOIN EMPLOYEE E ON E.EmployeeNumber = 0.EmployeeNumber
INNER JOIN CUSTOMER C ON C.CustomerNumber= 0.CustomerNumber
LIMIT 0,10