Fairly basic query (maybe needs subquery and or outer join)? - sql

I am not sure why my brain can't process this right now but it seems like this would be an easy query to write.
I have 2 tables:
- Orders
(pk)orderID
customername
orderDate
- OrderDetails
(pk)detailsID
orderID
productName
So when a new order is placed, one row gets added to Orders with customer info and in the orderDetails table a row gets added for each product ordered.
So picture these 2 layouts:
ORDERS
orderID customerName orderDate
1 John Smith 2/21/2020
2 Bill Adams 2/21/2020
3 Susan Conrad 2/21/2020
4 Amy Fetcher 2/21/2020
ORDERDETAILS
detailsID orderID productName
1 1 pillow
2 1 mattress
3 2 pillow
4 3 sheets
5 3 headboard
6 3 mattress
7 4 pillow
8 4 framed art
9 4 decals
10 4 plant stand
In my query, I want to select all from orders where the date = today. I am joining the ORDERDETAILS table because I want to know anytime I sold a pillow in my results.
I only want 1 row per order to be displayed in the results so in orderID 1 for example, they ordered a pillow and mattress. In the results under productName for that row, I want it to display pillow.
If someone didn't order a pillow, I still need the row to show for their order today and under productName I dont care what it shows if it's not a pillow. It can show nothing or one of the other product names they ordered.
I know this doesn't work:
SELECT ORDERS.*, ORDERDETAILS.*
FROM ORDERS INNER JOIN ORDERDETAILS ON ORDERS.orderID = ORDERDETAILS.orderID
WHERE productName = "pillow"
I am not sure if I need subqueries or some sort of outer left join maybe both?
Any help is much appreciated, thank you!

You can do it this way
select detailsID, a.orderID, productName, customerName, orderDate from
(
select detailsID, orderID, productName from ORDERDETAILS where productName = 'pillow'
union
select detailsID, orderID, productName from
(
select detailsID, orderID, productName, rank()over(partition by orderID order by detailsID) as _Rank from ORDERDETAILS
)a where _Rank = 1
and orderID not in (select orderID from ORDERDETAILS where productName = 'pillow')
) a
inner join Orders b on a.orderID = b.orderID
Output:
detailsID orderID productName customerName orderDate
1 1 pillow John Smith 2/21/2020
3 2 pillow Bill Adams 2/21/2020
4 3 sheets Susan Conrad 2/21/2020
7 4 pillow Amy Fetcher 2/21/2020

Since you're only interested in some order details, but all headers, you want a LEFT JOIN. And since you only want to filter the results from ORDERDETAILS, you'll want your filtering criteria in the JOIN, rather than in a WHERE clause that also applies to the ORDERS table.
SELECT ORDERS.*, ORDERDETAILS.*
FROM
ORDERS
LEFT JOIN ORDERDETAILS
ON ORDERS.orderID = ORDERDETAILS.orderID
AND productName = "pillow"
That should get you most of the way there. If there's a possibility of more than one detail record per order that includes pillow, then you'll want to use a sub-query against the details table to skinny it down further to just the information you're interested in. And at that point, I'd move the filter into the sub-query.

Related

Returning the TOP 20 ProductCodes (SKUs) in a Query using TOP 20 along with SUM

I have an Orders and an OrderDetails table. I am trying to get the TOP 20 selling ProductCodes by summing the quantity (QTY) of each ProductCode sold. I am having problems getting the query correct. At first, I was able to get the query to work using Count in place of SUM, but that was giving false results. Because you see below that would return BBB (4 units) as the winner, but actually we sold more of AAA (6 units).
Example data:
Orders table
ID
Rep
ShipDate
1
Amazon
8/29/2022
OrderDetails table
ProductCode
QTY
AAA
1
BBB
1
BBB
1
BBB
1
BBB
1
CCC
3
AAA
2
AAA
3
Below is my latest attempt. I thought I was getting close, but now I get prompted to enter a value for SumOfQty whenever I run the query. The OrderDetails.Packaging column is an additional column I would like to include in the output of the query, but not essential to getting the correct results. It seems like Access does not like an alias in the GROUP BY and ORDER BY.
SELECT TOP 20
SUM(OrderDetails.QTY) AS SumOfQTY,
OrderDetails.ProductCode, OrderDetails.Packaging
FROM
Orders
LEFT JOIN
OrderDetails ON Orders.OrderID = OrderDetails.OrderID
WHERE
(((Orders.ShipDate) BETWEEN #8/29/2022# AND #9/1/2022#)
AND ((Orders.Rep)="Amazon"))
GROUP BY
SumOfQTY, OrderDetails.ProductCode, OrderDetails.Packaging
ORDER BY
SumOfQTY DESC;
Thank you in advance,
Ira

SQL - Return group of records from different tables not including specific row

I am new at programming and SQL, so sorry if I do not include enough info.
[I have these 2 tables that are linked by an OrderID. Table1 includes OrderIDs and customer information such as FirstName, LastName, and Address. Table2 includes OrderIDs and order details such as ItemName, Price, and Quantity.
Each OrderID in Table2 might have multiple ItemName entries with the same OrderID.]1
CustInfo
OrderID FirstName LastName Address
1 Bob Pratt 123
2 Jane Doe 456
3 John Smith 789
4 Barbara Walters 147
Orders
OrderID ItemName Price Quantity
1 Milk 4.00 1
1 Eggs 5.00 2
2 Cheese 5.00 1
2 Bread 5.00 1
3 Milk 4.00 2
4 Yogurt 5.00 2
I'm trying to make a query that will send back a list of every Order, listing the OrderID and ItemName among other info, as long as the order doesn't include a specific type of item (which would be in ItemName). So if an OrderID contains 2 ItemName, one of which is the one I do not want, the entire order (OrderID) should not show up in my result.
For example, based off the img included, if I wanted to show all orders as long as they do not have Milk as an ItemName, the result should only show OrderID 2 and 4.
2 Cheese 5.00 1
2 Bread 5.00 1
4 Yogurt 5.00 2
This is what I have tried but this would return OrderIDs even though Milk is technically part of that OrderID.
SELECT OrderID, FirstName, LastName, ItemName, Price, Quantity
FROM CustInfo
JOIN Orders
ON CustInfo.OrderID = Orders.OrderID
WHERE ItemName != 'Milk'
Can you help?
select o.OrderID, o.ItemName, c.FirstName, c.LastName -- include other fields if needed
from Orders o
left join CustInfo c on o.OrderID = c.OrderID
where o.OrderID not in (
select OrderID from Orders where ItemName = 'Milk'
)
If you want the whole order to not show up, rather than just individual rows, you can use WHERE NOT EXISTS:
SELECT o.OrderID, d.ItemName, d.Price, d.Quantity
FROM Orders o
JOIN OrderDetails d ON o.OrderID=d.OrderID
WHERE NOT EXISTS
(
SELECT * FROM OrderDetails d2
WHERE o.OrderID=d2.OrderID
AND d.ItemName = 'Milk'
)
SELECT T1.OrderID, T1.FirstName, T1.LastName, T1.Address, T2.OrderID, T2.ItemName, T2.Price, T2.Quantity
FROM Table2 as T2
LEFT JOIN Table1 as T1 ON T1.OrderID = T2.OrderID
WHERE T2.OrderID <> (SELECT OrderID FROM Table2 WHERE ItemName='Milk');

SQL subquery and aggregate function

I am just learning about SQL subqueries, and I am a little confused as to why this isn't working. I have two tables, Order and OrderDetails. They both have an OrderID, and the OrderDetails has the fields OrderDetails.ProductID and OrderDetails.Quantity. So different orders can have different products, and on the OrderDetails table, there can be multiple rows for an OrderID if the Order has more than one different product.
so on the Orders Table it could be like
OrderID
10248
10249
10250
and in the OrderDetails Table:
OrderID | ProductID | Quantity
10248 | 11 | 12
10248 | 42 | 10
10248 | 72 | 5
etc
What I want to do is print out a list of all OrderIDs that have more than 1 product ordered.
I thought the following made sense:
Select Orders.OrderID
FROM Orders
WHERE Orders.OrderID = (Select OrderDetails.OrderID
FROM OrderDetails
GROUP BY OrderDetails.OrderID
HAVING (COUNT(OrderDetails.ProductID) > 1))
But this is certainly not working. Only one order ID is printed even though basically ever OrderID in the database has multiple products. I hope the way I asked this wasn't too confusing, it's my first time posting about SQL on here
This is all you need, your inner query.
Select OrderID
FROM OrderDetails
GROUP BY OrderID
HAVING COUNT(*) > 1
And if you want the other fields from the main Order table for these Orders with multiple products, then use the following.
SELECT *
FROM Orders
WHERE OrderId IN (
SELECT OrderID
FROM OrderDetails
GROUP BY OrderID
HAVING COUNT(*) > 1
)

Select data from multiple table where there may not necessarily be a match between some tables

Trying to return the rest of the matches from other tables even if one of two table doesn't have a match. Best I can explain this, sorry.
Given the following tables
customers table
customerid firstname lastname EmailAddress etc...
1 Tom Smith tsmith#abc.com
2 Mike Adams mikea#abc.com
etc...
orders table
orderid customerid orderdate etc..
1 1 11/8/2007 8:53:00 AM
2 1 11/8/2007 8:53:00 AM
3 2 11/8/2007 8:53:00 AM
4 3 11/8/2007 8:53:00 AM
5 4 11/8/2007 8:53:00 AM
6 3 11/8/2007 8:53:00 AM
7 5 11/8/2007 8:53:00 AM
8 3 11/8/2007 8:53:00 AM
orderdetails table
orderid productcode productname productprice quantity etc...
1 widget1 widget 1 5.00 5
2 widget2 widget 2 6.00 3
3 widget3 widget 3 7.00 2
etc...
product table
vendor_partno productcode productprice saleprice
wig1 widget1 10.00 7.50
wig3 widget3 8.00
etc...
As you can see in the product table that widget2 product is no longer in the inventory. I am trying to select all product previously purchased by a specific customer based on there e-mail address and customer id. I need to display all products they have purchased even if it is not longer available in the product table.
The following query only shows those that are still in the products table.
What I want is to return all info shown in the select and if corresponding product info in the product table doesn't exist then just return the data that does exist.
SELECT CONVERT(VARCHAR(10), orders.orderdate, 101) AS OrderDate,
orderdetails.orderid,
orders.customerid,
customers.emailaddress,
orderdetails.productcode AS orig_product_code,
orderdetails.productname,
orderdetails.productprice,
orderdetails.quantity,
products_joined.vendor_partno,
products_joined.productcode,
products_joined.productprice AS current_reg_price,
products_joined.saleprice AS current_sale_price
FROM orders,
customers,
orderdetails,
products_joined
WITH (NOLOCK)
WHERE
orders.orderid = orderdetails.orderid
AND customers.customerid = orders.customerid
AND orderdetails.productcode = products_joined.productcode
AND customers.emailaddress = 'tsmith#abc.com'
AND customers.customerid = '1'
ORDER BY orders.orderid DESC
Right now what is returned is
orderdate orderid customerid emailaddress orig_product_code product name product price quantity vendor_partno productcode current_reg_price current_sale_price
11/8/2007 1 1 tsmith#abc.com widget1 widget 1 5.00 5 wig1 widget1 10.00 7.50
What I would like returned is
orderdate orderid customerid emailaddress orig_product_code product name product price quantity vendor_partno productcode current_reg_price current_sale_price
11/08/2007 1 1 tsmith#abc.com widget1 widget 1 5.00 5 wig1 widget1 10.00 7.50
11/08/2007 2 1 tsmith#abc.com widget2 widget 2 6.00 3
Obviously the reason for this is
orderdetails.productcode = products_joined.productcode
but i am not sure how to get fix this.
Any help would be appreciated.
I think you just need an outer join with the products_joined table like below.
Also it would be a good idea to use modern JOIN syntax; ie. put all join conditions into the JOIN clause rather than the WHERE clause, like so:
SELECT CONVERT(VARCHAR(10), orders.orderdate, 101) AS OrderDate,
orderdetails.orderid,
orders.customerid,
customers.emailaddress,
orderdetails.productcode AS orig_product_code,
orderdetails.productname,
orderdetails.productprice,
orderdetails.quantity,
products_joined.vendor_partno,
products_joined.productcode,
products_joined.productprice AS current_reg_price,
products_joined.saleprice AS current_sale_price
FROM orders
join customers
on customers.customerid = orders.customerid
join orderdetails
on orders.orderid = orderdetails.orderid
left join products_joined
on orderdetails.productcode = products_joined.productcode
WHERE customers.emailaddress = 'tsmith#abc.com'
AND customers.customerid = '1'
ORDER BY orders.orderid DESC

Find the the value of one field that matches the maximum value of data in another field

I'm trying to write a query that gets the value of one field that's associated with the maximum value of another field (or fields). Let's say I have the following table of data:
OrderID CustomerID OrderDate LocationID
1 4 1/1/2001 1001
2 4 1/2/2001 1003
3 4 1/3/2001 1001
4 5 1/4/2001 1001
5 5 1/5/2001 1001
6 5 1/6/2001 1003
7 5 1/7/2001 1002
8 5 1/8/2001 1003
9 5 1/8/2001 1002
Grouping by CustomerID, I want to get the maximum OrderDate and then the LocationID associated with whatever is the maximum OrderDate. If there are several records that share the maximum order date, then take the LocationID associated with the maximum OrderID from among those records with the maximum date.
The final set of data should look like this:
CustomerID OrderDate LocationID
4 1/3/2001 1001
5 1/8/2001 1002
I had been trying to write a query with lots of nested subqueries and ugly joins, but I'm not really getting anywhere. What SQL do I need to write to help me get this result.
with cte As
(
select *,
row_number() over (partition by CustomerID
order by OrderDate desc, OrderId desc) as rn
from yourtable
)
select CustomerID, OrderDate,LocationID
from cte
where rn=1;
SELECT
C.Name,
C.CustomerID,
X.*
FROM
Customers C
CROSS APPLY (
SELECT TOP 1 OrderDate, LocationID
FROM Orders O
WHERE C.CustomerID = O.CustomerID
ORDER BY OrderDate Desc, OrderID Desc
) X
If you will pull any columns from the Customers table, this will probably outperform other methods.
If not, then the Row_Number answer, pulling only from Orders, will probably be best. But if you restrict by Customer in any way, then the CROSS APPLY will again be best. Possibly by a big margin.
The trick is to use a subquery as a value, not as a join:
select customerId,orderDate,locationId
from orders o1
where orderDate = (
select top 1 orderdate
from orders o2
where o1.customerId = o2.customerId
order by orderdate desc
)