SQL subquery and aggregate function - sql

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
)

Related

multiple two column values with grouping by a column

I've two tables tblOrder and tblOrderDetails. I want to get order no, total price per order (Quantity*UnitCost) and OrderDate as given below.
Order No
Total
OrderDate
ORD 1
3000
01/01/2021
ORD 2
2750
01/03/2021
What I've tried is giving me quantity is not a part of aggregate function.
SELECT tblOrder.OrderNo, tblOrderDetails.UnitCost*tblOrderDetails.Quantity AS Total, OrderDate
FROM tblOrderDetails INNER JOIN tblOrder ON tblOrderDetails.OrderId = tblOrder .OrderId
GROUP BY tblOrder.OrderNo;
Table structures and data
Table tblOrder:
OrderId
OrderNo
OrderDate
1
ORD 1
01/01/2021
2
ORD 2
01/03/2021
Table tblOrderDetails:
OrderDetailId
Quantity
UnitCost
OrderId
1
100
30
1
2
50
40
2
2
10
15
2
2
20
30
2
select o.OrderNo
,od.total
,o.OrderDate
from
(
select OrderId
,sum(Quantity*UnitCost) as total
from tblOrderDetails
group by OrderId
) od join tblOrder o on o.OrderId = od.OrderId
OrderNo
total
OrderDate
ORD 1
3000
2021-01-01
ORD 2
2750
2021-01-03
Fiddle
Your requirements are not 100% clear, but maybe, you can just do this, without any subquery:
SELECT tblOrder.OrderNo,
SUM(tblOrderDetails.UnitCost*tblOrderDetails.Quantity) AS Total,
OrderDate
FROM tblOrderDetails
INNER JOIN tblOrder ON tblOrderDetails.OrderId = tblOrder.OrderId
GROUP BY tblOrder.OrderNo,OrderDate;
To see the difference to Danny's answer - which might also be fine - have a look here: db<>fiddle

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

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.

Getting total amount of orders using sql query

Hi lets say i have the following tow tables
first table is Orders which consist of following columns
Order_ID OrderDate
1 2016-01-20
2 2016-01-21
and the second table is OrderDetails which consist of following columns
Order_ID ProductID UnitPrice Quantity
1 1 5.00 1
1 3 20.00 3
2 2 10.00 2
How can i get the following out put
Order_ID OrderDate OrderTotalamount
1 2016-01-20 65.00
2 2016-01-21 20.00
Would you mind trying something like this out?
SELECT ORD.Order_ID, ORD.OrderDate, SUM(ORDD.UnitPrice * ORDD.Quanity) AS OrderTotalAmount
FROM Orders ORD
INNER JOIN OrderDetails ORDD
ON ORDD.Order_ID = ORD.Order_ID
GROUP BY ORD.Order_ID, ORD.OrderDate
You can try this
SELECT a.Order_ID, a.OrderDate, SUM(Quantity*UnitPrice) as OrderTotalamout
FROM Orders a JOIN OrderDetains ON (a.Order_ID = OrderDetails.Order_ID)
GROUP BY 1, 2;
I hope it works fine for you.
YOU CAN USE
SELECT TABLE1.Order_ID, TABLE1.OrderDate, SUM(TABLE2.Quantity*TABLE2.UnitPrice) as TotalPrice
FROM TABLE1 JOIN TABLE2 WHERE(TABLE1.Order_ID = TABLE2.Order_ID);

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
)