I am trying to return all the orders from a particular person, I have two tables, a person and an order and using a join, it does return orders of the person but for each order it returns the square amount of that order, for example if there two orders that are the same it will return 4 of those orders.
SELECT
o.*
FROM
orderr o
LEFT JOIN person p
ON p.personID = o.personID
WHERE p.first_name = ?
try this
SELECT DISTINCT
o.*
FROM
orderr o
LEFT JOIN person p
ON p.personID = o.personID
WHERE p.first_name = ?
Related
I am trying to solve the following query, there's a few additional parameters, but these are the main attributes required:
Provide the product details, which vendors supply these products, and what was the last date these products were ordered by customers.
I have my original query below which gets me 90% of the way. I just can't seem to figure out how to display the last order date by customer per individual product. I've tried embedding (select max(o.OrderDate) from orders as o) into my select statement, but it only displays the latest order date of all of the products, not the individual per product last order date (e.g., all dates listed are 01/01/2020 when I know other products' last order date was before this date).
Apologies, I do not have enough rep to post pictures in line with text, therefore I have attached pictures of table structure and my query.
SQL Query
Table structure
Query:
select distinct p.ProductNumber, p.ProductName, p.RetailPrice, p.QuantityOnHand, v.VendName,
(select max(o.OrderDate)
from orders as o)
as LastOrderDateByCust
from ((((orders as o
inner join order_details as od
on od.OrderNumber = o.OrderNumber)
inner join products as p
on p.ProductNumber = od.ProductNumber)
inner join product_vendors as pv
on p.ProductNumber = pv.ProductNumber)
inner join vendors as v
on pv.VendorID = v.VendorID)
where p.QuantityOnHand < '10'
order by LastOrderDateByCust DESC;`
figured it out on my own I believe:
select distinct p.ProductNumber, p.ProductName, c.CategoryDescription, p.RetailPrice, pv.WholesalePrice, p.QuantityOnHand, v.VendName, pv.DaysToDeliver, (select max(o.OrderDate)
from orders as o
inner join order_details as od
on o.OrderNumber = od.OrderNumber
where od.ProductNumber = p.ProductNumber)
as LastOrderDateByCust
from (((((orders as o
inner join order_details as od
on o.OrderNumber = od.OrderNumber)
inner join products as p
on p.ProductNumber = od.ProductNumber)
inner join product_vendors as pv
on p.ProductNumber = pv.ProductNumber)
inner join vendors as v
on pv.VendorID = v.VendorID)
inner join categories as c
on p.CategoryID = c.CategoryID)
where od.ProductNumber = p.ProductNumber and p.QuantityOnHand < '10'
order by p.ProductNumber;`
THE SCENARIO
Three tables:
orders
customers
delivery_addresses
THE RELATIONS
orders.customerId points to customers.id
orders.customerId points to delivery_addresses.customerId
THE GOAL
Is to select any items from orders which meet a condition of a column in either customers or delivery_addresses. In a more practical sense: i want to search for orders of customers with a certain name (or zip code or whatever) stored in either customers or delivery_addresses.
MY ATTEMPTS
I am not very good with joins and the logic behind them, but after some solid research i came up with this:
SELECT orders.* FROM orders INNER JOIN customers ON orders.customerId = customers.id INNER JOIN delivery_addresses ON orders.customerId = delivery_addresses.customerId WHERE ((customers.first_name LIKE "%max%") OR (delivery_addresses.first_name LIKE "%max%"))
This, however, does not work. I am only getting results if there is a corresponding entry in delivery_addresses, but not if there is only one in customers.
Thus, if i shorten the query like this:
SELECT orders.* FROM orders INNER JOIN customers ON orders.customerId = customers.id WHERE (customers.first_name LIKE "%max%")
I do get the correct results.
CONCLUSIONS
I am certain that i am missing a crucial point in regard to my understanding of how joins work. But i cannot figure out what it is.
Here INNER JOIN works like a filter
SELECT orders.*
FROM orders
INNER JOIN customers ON orders.customerId = customers.id
INNER JOIN delivery_addresses ON orders.customerId = delivery_addresses.customerId
WHERE ((customers.first_name LIKE "%max%")
OR (delivery_addresses.first_name LIKE "%max%"))
It could be rewritten using LEFT JOIN:
SELECT orders.*
FROM orders
INNER JOIN customers ON orders.customerId = customers.id
LEFT JOIN delivery_addresses ON orders.customerId = delivery_addresses.customerId
WHERE customers.first_name LIKE '%max%'
OR delivery_addresses.first_name LIKE'%max%
Alternatively using UNION:
SELECT orders.*
FROM orders
INNER JOIN customers ON orders.customerId = customers.id
WHERE customers.first_name LIKE '%max%'
UNION
SELECT orders.*
FROM orders
INNER JOIN customers ON orders.customerId = customers.id
INNER JOIN delivery_addresses ON orders.customerId = delivery_addresses.customerId
WHERE delivery_addresses.first_name LIKE'%max%
You can do it with LEFT joins and all the conditions in the ON clauses:
SELECT o.*
FROM orders o
LEFT JOIN customers c ON o.customerId = c.id AND c.first_name LIKE "%max%"
LEFT JOIN delivery_addresses d ON o.customerId = d.customerId AND d.first_name LIKE "%max%"
WHERE c.id IS NOT NULL OR d.customerId IS NOT NULL
You may have to add DISTINCT after SELECT if you are getting duplicate rows:
SELECT DISTINCT o.*
....................
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 have a database structure with the following setup:
po: id, stockNumber, factoryId, other columns
order: id, stockNumber, factoryId, other columns
stock_number: id, stockNumber, groupId
factory: id, name, groupId
The important part here is the stock_number/factory tables. The groupId column is just an integer and if two or more rows in the table have the same value then their stock numbers/factory are considered the same. Typically this is used for different sizes of the same product.
What I'd like to do is write a query that will join "order" to "po" through the group of stock_number and factory so I can find orders with no matching po. Also the factory has to match the same way.
I have this query if I have a specific stock number/factory in mind but I'd like to update it to query the whole orders table for me:
SELECT id
FROM order
WHERE
styleNumber IN (SELECT a.stockNumber FROM stock_number a INNER JOIN stock_number b ON a.groupId = b.groupId or a.id = b.id WHERE b.stockNumber = '123')
AND factoryId IN (SELECT a.submitter_id FROM submitter a INNER JOIN submitter b ON a.groupId = b.groupId OR a.submitter_id = b.submitter_id WHERE b.SUBMITTER_ID = 'alpha');
EDIT: I came up with this query which I think might be on the right track. It only joins in the stock number so it doesn't do factory yet. Can anyone confirm if I'm going in the correct direction:
SELECT *
FROM order o
LEFT JOIN stock_number s_o ON o.stockNumber = s_o.stockNumber
LEFT JOIN stock_number s_p ON s_o.groupId = s_p.groupId
LEFT JOIN po p ON s_p.stockNumber = p.stockNumber
WHERE p.id IS NULL;
Just join all the tables.
select o.id
FROM order AS o
JOIN stock_number AS sn ON sn.stockNumber = o.stockNumber
JOIN submitter AS su ON ON o.factoryId = su.submitter_id
You could use an anti-join pattern. In this example, it looks complicated because of the two relationship tables. But a query something like this:
SELECT o.id
, o.stockNumber
, o.factoryId
FROM `order` o
LEFT
JOIN `stock_number` s
ON s.stockNumber = o.stockNumber
LEFT
JOIN `factory` f
ON f.id = o.factoryId
AND f.groupId = s.groupId
LEFT
JOIN `po` p
ON p.stockNumber = s.stockNumber
AND p.factoryId = f.id
WHERE p.id IS NULL
The anti-join pattern is easier to visualize with a simpler example. Say you had the order table (as in your example), and an order_line table, with rows related to the order table by the order_id column.
order_line: id, order_id, othercolumns
To get order along with matching order_line rows:
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
JOIN `order_line` l
ON l.order_id = o.id
To include rows from order that don't have any matching rows in order_line, we can use an outer join. We add the LEFT keyword:
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
LEFT
JOIN `order_line` l
ON l.order_id = o.id
That gets all rows from order, including rows that don't have a matching row in order_line. The trick now is to exclude all the rows that have a matching row. For any rows that didn't have a match, the columns from order_line will be NULL. So we can add a test in the WHERE clause, to exclude rows that had a match.
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
LEFT
JOIN `order_line` l
ON l.order_id = o.id
WHERE l.order_id IS NULL
That gets us rows from order that don't have a matching row in order_line.
We can use this same pattern in a more complicated query. We use outer join operations, and rows from order that don't have a matching row in po will have NULL values for the columns from po.
I have three tables: store_products, store_orders and store_orders_products. store_products contains all of the products, store_orders contains the order information, and store_orders_products contains each individual product in that order.
I'm building an Orders page that will list each order placed. It will have the username of the person who placed the order, the date, the total amount and the name of each product.
This query gets me everything I want, except it returns only ONE product name. I want to have each product in a comma separated string. I've tried subqueries, COALESCE, CONCAT, IN(), etc, but I'm not getting the results I want.
SELECT
o.id AS order_id
, o.order_total
, o.created_at
, u.username
, u.avatar
, sp.name
FROM store_orders o
JOIN store_orders_contents c ON o.id=c.oid
JOIN store_products p ON p.id=c.pid
JOIN users u ON u.id=o.consumer_uid
JOIN store_products sp ON sp.id=c.pid
WHERE p.uid=3
GROUP BY order_id
I think you want group_concat():
SELECT o.id AS order_id, o.order_total, o.created_at, u.username, u.avatar,
group_concat(sp.name )
FROM store_orders o JOIN
store_orders_contents c
ON o.id = c.oid JOIN
store_products p
ON p.id = c.pid JOIN
users u
ON u.id = o.consumer_uid JOIN
store_products sp
ON sp.id = c.pid
WHERE p.uid = 3
GROUP BY order_id;
I don't understand why you are joining to store_products twice, but I assume you know what you are doing with the joins.