Get ID using join based on most recent date - sql

I have 2 tables:
Orders
order_id total
1 5
Invoices
order_id invoice_id ship_date
1 a 1/1/2020
1 b 2/2/2020
I need to get the earliest ship date and the invoice_id of the latest date. So the query should return the following:
order_id total latest_invoice_id earliest_ship_date
1 5 b 1/1/2020
Here's my query so far:
SELECT
order_id,
total,
earliest_ship_date,
latest_invoice_id
FROM Orders o
INNER JOIN (SELECT
order_id,
min(ship_date) as earliest_ship_date,
max(invoice_id) as latest_invoice_id
FROM Invoices
GROUP BY order_id) i ON o.order_id = i.order_id
Of course this doesn't work because all I do is get the highest invoice_id using alphabetical order. How can I get the invoice ID of the latest ship date in this case?

You can use window functions and conditional aggregation. I find that a lateral join is handy here:
select o.*, i.*
from orders o
cross apply (
select
max(case when rn = 1 then invoice_id end) latest_invoice_id,
min(ship_date) earliest_ship_date
from (
select i.*,
row_number() over(partition by order_id order by ship_date desc) rn
from invoices i
where i.order_id = o.order_id
) i
) i

You can go for ranking and then arrive at the result
;WITH CTE_Invoices AS
(SELECT o.Order_Id,O.total, i.invoice_id,i.ship_date
ROW_NUMBER() OVER(PARTITION BY Order_Id ORDER BY ship_date DESC) as rnk_latestInvoice,
ROW_NUMBER() OVER(PARTITION BY Order_Id ORDER BY ship_date) AS rnk_shipdate
FROM Orders as o
INNER JOIN Invoices as i
ON i.Order_Id = o.Order_ID)
select Order_Id, total , MAX(CASE WHEN rnk_latestInvoice = 1 THEN invoice_id END) as Latest_Invoice_id,
MAX(CASE WHEN rnk_shipdate = 1 THEN ship_date END) as earliest_shipdate
FROM CTE_Invoices
GROUP BY Order_Id, total

Related

SQL - Finding Customer's largest Location by Order $

I have a table with customer IDs, location IDs, and their order values. I need to select the location ID for each customer with the largest spend
Customer | Location | Order $
1 | 1A | 100
1 | 1A | 20
1 | 1B | 100
2 | 2A | 50
2 | 2B | 20
2 | 2B | 50
So I would get
Customer | Location | Order $
1 | 1A | 120
2 | 2B | 70
I tried something like this:
SELECT
a.CUST
,a.LOC
,c.BOOKINGS
FROM (SELECT DISTINCT TOP 1 b.CUST, b.LOC, sum(b.ORDER_VAL) as BOOKINGS
FROM ORDER_TABLE b
GROUP BY b.CUST, b.LOC
ORDER BY BOOKINGS DESC) as c
INNER JOIN ORDER_TABLE a
ON a.CUST = c.CUST
But that just returns the top order.
Just use variables to emulate ROW_NUM()
DEMO
SELECT *
FROM ( SELECT `Customer`, `Location`, SUM(`Order`) as `Order`,
#rn := IF(#customer = `Customer`,
#rn + 1,
IF(#customer := `Customer`, 1, 1)
) as rn
FROM Table1
CROSS JOIN (SELECT #rn := 0, #customer := '') as par
GROUP BY `Customer`, `Location`
ORDER BY `Customer`, SUM(`Order`) DESC
) t
WHERE t.rn = 1
Firs you have to sum the values for each location:
select Customer, Location, Sum(Order) as tot_order
from order_table
group by Customer, Location
then you can get the maximum order with MAX, and the top location with a combination of group_concat that will return all locations, ordered by total desc, and substring_index in order to get only the top one:
select
Customer,
substring_index(
group_concat(Location order by tot_order desc),
',', 1
) as location,
Max(tot_order) as max_order
from (
select Customer, Location, Sum(Order) as tot_order
from order_table
group by Customer, Location
) s
group by Customer
(if there's a tie, two locations with the same top order, this query will return just one)
This seems like an order by using aggregate function problem. Here is my stab at it;
SELECT
c.customer,
c.location,
SUM(`order`) as `order_total`,
(
SELECT
SUM(`order`) as `order_total`
FROM customer cm
WHERE cm.customer = c.customer
GROUP BY location
ORDER BY `order_total` DESC LIMIT 1
) as max_order_amount
FROM customer c
GROUP BY location
HAVING max_order_amount = order_total
Here is the SQL fiddle. http://sqlfiddle.com/#!9/2ac0d1/1
This is how I'd handle it (maybe not the best method?) - I wrote it using a CTE first, only to see that MySQL doesn't support CTEs, then switched to writing the same subquery twice:
SELECT B.Customer, C.Location, B.MaxOrderTotal
FROM
(
SELECT A.Customer, MAX(A.OrderTotal) AS MaxOrderTotal
FROM
(
SELECT Customer, Location, SUM(`Order`) AS OrderTotal
FROM Table1
GROUP BY Customer, Location
) AS A
GROUP BY A.Customer
) AS B INNER JOIN
(
SELECT Customer, Location, SUM(`Order`) AS OrderTotal
FROM Table1
GROUP BY Customer, Location
) AS C ON B.Customer = C.Customer AND B.MaxOrderTotal = C.OrderTotal;
Edit: used the table structure provided
This solution will provide multiple rows in the event of a tie.
SQL fiddle for this solution
How about:
select a.*
from (
select customer, location, SUM(val) as s
from orders
group by customer, location
) as a
left join
(
select customer, MAX(b.tot) as t
from (
select customer, location, SUM(val) as tot
from orders
group by customer, location
) as b
group by customer
) as c
on a.customer = c.customer where a.s = c.t;
with
Q_1 as
(
select customer,location, sum(order_$) as order_sum
from cust_order
group by customer,location
order by customer, order_sum desc
),
Q_2 as
(
select customer,max(order_sum) as order_max
from Q_1
group by customer
),
Q_3 as
(
select Q_1.customer,Q_1.location,Q_1.order_sum
from Q_1 inner join Q_2 on Q_1.customer = Q_2.customer and Q_1.order_sum = Q_2.order_max
)
select * from Q_3
Q_1 - selects normal aggregate, Q_2 - selects max(aggregate) out of Q_1 and Q_3 selects customer,location, sum(order) from Q_1 which matches with Q_2

T-SQL: Select partitions which have more than 1 row

I've managed to use this query
SELECT
PartGrp,VendorPn, customer, sum(sales) as totalSales,
ROW_NUMBER() OVER (PARTITION BY partgrp, vendorpn ORDER BY SUM(sales) DESC) AS seqnum
FROM
BG_Invoice
GROUP BY
PartGrp, VendorPn, customer
ORDER BY
PartGrp, VendorPn, totalSales DESC
To get a result set like this. A list of sales records grouped by a group, a product ID (VendorPn), a customer, the customer's sales, and a sequence number which is partitioned by the group and the productID.
PartGrp VendorPn Customer totalSales seqnum
------------------------------------------------------------
AGS-AS 002A0002-252 10021013 19307.00 1
AGS-AS 002A0006-86 10021013 33092.00 1
AGS-AS 010-63078-8 10020987 10866.00 1
AGS-SQ B71040-39 10020997 7174.00 1
AGS-SQ B71040-39 10020998 2.00 2
AIRFRAME 0130-25 10017232 1971.00 1
AIRFRAME 0130-25 10000122 1243.00 2
AIRFRAME 0130-25 10008637 753.00 3
HARDWARE MS28775-261 10005623 214.00 1
M250 23066682 10013266 175.00 1
How can I filter the result set to only return rows which have more than 1 seqnum? I would like the result set to look like this
PartGrp VendorPn Customer totalSales seqnum
------------------------------------------------------------
AGS-SQ B71040-39 10020997 7174.00 1
AGS-SQ B71040-39 10020998 2.00 2
AIRFRAME 0130-25 10017232 1971.00 1
AIRFRAME 0130-25 10000122 1243.00 2
AIRFRAME 0130-25 10008637 753.00 3
Out of the first result set example, only rows with VendorPn "B71040-39" and "0130-25" had multiple customers purchase the product. All products which had only 1 customer were removed. Note that my desired result set isn't simply seqnum > 1, because i still need the first seqnum per partition.
I would change your query to be like this:
SELECT PartGrp,
VendorPn,
customer,
sum(sales) as totalSales,
ROW_NUMBER() OVER (PARTITION BY partgrp,vendorpn ORDER BY SUM(sales) DESC) as seqnum,
COUNT(1) OVER (PARTITION BY partgrp,vendorpn) as cnt
FROM BG_Invoice
GROUP BY PartGrp,VendorPn, customer
HAVING cnt > 1
ORDER BY PartGrp,VendorPn, totalSales desc
You can try something like:
SELECT PartGrp,VendorPn, customer, sum(sales) as totalSales,
ROW_NUMBER() OVER (PARTITION BY partgrp,vendorpn ORDER BY SUM(sales) DESC) as seqnum
FROM BG_Invoice
GROUP BY PartGrp,VendorPn, customer
HAVING seqnum <> '1'
ORDER BY PartGrp,VendorPn, totalSales desc
WITH CTE AS (
SELECT
PartGrp,VendorPn, customer, sum(sales) as totalSales,
ROW_NUMBER() OVER (PARTITION BY partgrp, vendorpn ORDER BY SUM(sales) DESC) AS seqnum
FROM
BG_Invoice
GROUP BY
PartGrp, VendorPn, customer)
SELECT DISTINCT
a.*
FROM
CTE a
JOIN
CTE b
ON a.PartGrp = b.PartGrp
AND a.VendorPn = b.VendorPn
WHERE
b.seqnum > 1
ORDER BY
a.PartGrp,
a.VendorPn,
a.totalSales DESC;

Get top n occurences based on related table value

I have a table Orders (Id, OrderDate, CreatorId) and a table OrderLines (Id, OrderId, OwnerIdentity, ProductId, Amount)
Scenario is as follows: Someone opens up an Order and other users can then place their product orders on that order. Those users are the OwnerId of OrderLines.
I need to retrieve the top 3 latest orders that a user has placed an order on and display all of his orders placed, to give him an insight in his personal recent orders.
So my end result would be something like
OrderId | ProductId | Amount
----------------------------
1 | 1 | 2
1 | 7 | 1
1 | 2 | 5
4 | 4 | 3
4 | 1 | 2
8 | 4 | 1
8 | 9 | 2
Select o.Id as OrderId, ol.ProductId, ol.Amount from Orders o
inner join OrderLines ol
on o.Id = ol.OrderId where o.Id in
(Select top 3 OrderId from Orders where OwnerId = #OwnerId)
Order By o.OrderDate desc
You can add date time column to OrderLines table to query latest personal orders and then update the code by moving "order by OrderDate desc" section to sub select query.
select * from
(
select OrderId, ProductId, Amount
row_number() over (partition by OrderID order by Orders.OrderDate) as rn
from OrderLines
join Orders
on OrderLines.OrderId = Orders.Id
where OwnerIdentity = x
) lskdfj
where rn <= 3
Try the below query:
SELECT OL.OrderId, OL.ProductID, OL.Amount
FROM OrderLines OL WHERE OL.OrderId IN
(
SELECT TOP 3 O.OrderID FROM orders O LEFT JOIN OrderLines OL2
ON OL2.orderId=O.OrderID
WHERE OL2.OwnerIdentity =...
ORDER BY O.OrderDate DESC
) AND WHERE OL.OwnerIdentity =...
;WITH cte AS (
SELECT ol.OrderId, ol.ProductId, ol.Amount,
ROW_NUMBER()OVER (PARTITION BY ol.OrderId ORDER BY o.OrderDate DESC) rn
FROM OrderLines ol
JOIN Orders o ON ol.OrderId = o.Id
WHERE OwnerIdentity = #OwnerId
)
SELECT OrderId, ProductId, Amount
FROM cte
WHERE rn <= 3

Group By / Having correlated subquery

I am trying to solve this query, I'm really lost
least one order in which the quantity they ordered was greater than
the average quantity of all other orders.
My tables are:
Customer
Cust_ID | CustName | Region | Phone
Orders
Ordernum | Cust_ID | Item_ID | Quantity
Vendor
Vendor_ID | Item_ID | Costs | Region
stock
Item_ID | Description | Price | On_hand
Database schema is:
Customer (Cust_ID, CustName, Region, Phone)
Orders (Ordernum, Cust_ID, Item_ID, Quantity) Foreign key Cust_ID
references Customer Not Null, On Delete Restrict
Foreign key Item_ID references Stock Not Null, On Delete Restrict
Stock (Item_ID, Description, Price, On_hand)
Vendor (Vendor_ID, Item_ID, Cost, Region)
Foreign key Item_ID references Stock Not null, On Delete Restrict
Here is what I have tried so far. What am I doing wrong?
select custname, phone, count(distinct(item_id)), sum(quantity)
from customer c, orders o
Where c.cust_id= o.cust_id and count (o.ordernum) >= 2
group by cust_id
having sum (quantity) >
(select avg(quantity)
from orders o2
where o.item_id != o2.item_id)
order by custname;
I re-wrote my code and this is what I came up with. I'm really lost:
select c1.custname, phone, count(distinct(o.item_id)), sum(quantity) as quantity
from customer c1, orders o
Where c1.cust_id= o.cust_id
group by c1.cust_id, custname, phone, quantity
Having 2>=
(select count(o1.item_id)
From orders o1
where c1.cust_ID = o1.cust_ID)
AND sum(quantity) >
(select AVG(quantity)
from orders o2
Where c1.cust_ID != o2.cust_ID AND o.Item_ID = o2.Item_ID)
order by custname;
Try this query
select a.cust_id, count(distinct Item_ID) as itemOrderd,
sum(b.quantity) as sum
from
customer a
inner join
orders b
on a.cust_id=b.cust_id
group by a.cust_id
having count(distinct ordernum) > 1
and max(b.quantity) > avg(b.quantity)
Fiddle
| CUST_ID | ITEMORDERD | SUM |
|---------|------------|-----|
| 1 | 2 | 61 |
I think it will be:
SELECT MAX(c.CustName) as CustName,
MAX(c.Phone) as Phone,
COUNT(DISTINCT o.Item_ID) as CountOfDistinctItems,
SUM(o.Quantity) as SumOfQuantity
FROM Customer as c
JOIN Orders as o on c.Cust_ID=o.Cust_ID
JOIN (SELECT Ordernum, SUM(Quantity) as Order_q_sum
FROM Orders
GROUP BY Ordernum) as oq
ON o.Ordernum=oq.Ordernum
GROUP BY c.Cust_ID
HAVING COUNT(DISTINCT o.Ordernum)>=2
AND
MAX(oq.Order_q_sum)>
(
SELECT AVG(SumQuantity) FROM
(SELECT SUM(Quantity) SumQuantity FROM Orders GROUP BY OrderNum) as t1
)
HOPE THIS CLOSER TO YOUR NEEDS
SELECT x.custname,
x.phone,
Sum(x.itemcnt),
Sum(x.quantity)
FROM (SELECT c.custname,
c.phone,
1 AS itemCnt,
Sum(o.quantity),
Isnull((SELECT Avg(y.quantity)
FROM orders y
WHERE y.cust_id = o.cust_id), 0.00) AvgQty
FROM customer c
LEFT OUTER JOIN orders o
ON c.cust_id = o.cust_id
GROUP BY c.custname,
c.phone,
o.item_id) x
GROUP BY x.custname,
x.phone
HAVING Sum(x.itemcnt) > 1
AND Sum(x.quantity) > x.avgqty

SQL Query Flattens Order and Top Order Items

I need help building a SQL query that returns a flattened result of the top 2 items in an order.
The tables and relevant fields are as follows:
Order OrderItem
------- -----------
orderId orderId
productCode
quantity
I'm looking for the desired result set:
[orderId] [productCode1] [quantity1] [productCode2] [quantity2]
---------- -------------- ----------- -------------- -----------
o123 p134 3 p947 1
o456 p384 2 p576 1
The results would be grouped by orderId from Order, with the TOP 2 productCode from quantity from OrderItem. I don't care which TOP 2 get returned, just need any two.
Any help would be greatly appreciated.
select
o.orderId,
max(case when row_num = 1 then oi.ProductCode end) as ProductCode1,
max(case when row_num = 1 then oi.Quantity end) as Quantity1,
max(case when row_num = 2 then oi.ProductCode end) as ProductCode2,
max(case when row_num = 2 then oi.Quantity end) as Quantity2
from Order as o
outer apply (
select top 2
oi.*, row_number() over (order by oi.productCode) as row_num
from OrderItem as oi
where oi.orderId = o.orderId
) as oi
group by o.orderId
You can do this with conditional aggregation and the window function row_number():
select orderId,
max(case when seqnum = 1 then ProductCode end) as ProductCode1,
max(case when seqnum = 1 then Quantity end) as Quantity1,
max(case when seqnum = 2 then ProductCode end) as ProductCode2,
max(case when seqnum = 2 then Quantity end) as Quantity2
from (select oi.*,
row_number() over (partition by orderId order by quantity desc) as seqnum
from OrderItem oi
) oi
group by orderId;
Assuming you are using SQL Server 2005 or later you can create a CTE ordered by ProductCode, then in a second CTE take the rows with rank 2 and last join them to Orders
;WITH OrderItem1 AS
(
SELECT OrderID, productCode AS productCode1, quantity AS quantity1,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY productCode) AS RowID
FROM [OrderItem]
),
OrderItem2 AS
(
SELECT OrderID, productCode1 AS productCode2, quantity1 AS quantity2
FROM [OrderItem1]
WHERE RowID = 2
)
SELECT [Order].OrderID, [OrderItem1].[productCode1], [OrderItem1].quantity1,
[OrderItem2].productCode2, [OrderItem2].quantity2
FROM [Order]
INNER JOIN OrderItem1 ON [Order].OrderID = OrderItem1.OrderID
LEFT JOIN OrderItem2 ON [Order].OrderID = OrderItem2.OrderID
WHERE OrderItem1.RowID = 1