Retrieve a column value without group by statement in SQL - sql

I need SellerID also in my select statement for them who is having minimum price and maximum price. Below is the query; please tell what change is required??
Select count(Id) TotalSeller,
min(price) as MinPrice, ***SellerID_for_min_price***,
max(price) as MaxPrice, ***SellerID_for_max_price***
from ProdPrice
where prodPriceId=1212
Data:
ProdId SellerID Price
1212 65 34740
1212 20 34855
1212 88 37299
1212 69 38490
1212 108 39990
1212 35 39999
1212 96 40990

To do this sort of query, you want to use row_number() to identify the rows with the smallest and largest values:
Select count(Id) TotalSeller, min(price) as MinPrice,
min(case when seqnum_min = 1 then id end) as SellerIdMin,
max(price),
min(case when seqnum_max = 1 then id end) as SellerIdMax
from (select pp.*,
row_number() over (partition by prodPriceId order by price) as seqnum_min,
row_number() over (partition by prodPriceId order by price desc) as seqnum_max
from ProdPrice pp
where prodPriceId=1212
) pp
To get the names, you can do the join in the subquery:
Select count(Id) TotalSeller, min(price) as MinPrice,
min(case when seqnum_min = 1 then SellerName end) as SellerNameMin,
max(price),
min(case when seqnum_max = 1 then SellerName end) as SellerNameMax
from (select pp.*, s.SellerName,
row_number() over (partition by prodPriceId order by price) as seqnum_min,
row_number() over (partition by prodPriceId order by price desc) as seqnum_max
from ProdPrice pp join
Sellers s
on pp.id = s.id
where prodPriceId=1212
) pp

Related

Get NULL value when using an aggregate function

Here is the tables:
https://dbfiddle.uk/markdown?rdbms=sqlserver_2019&fiddle=effc94afe681b2dfdb3e2c02c2b005ea
I want to find the average Total Amount for last 3 values (I mean the last 3 OrderID) for each customer. If customer doesn't have 3 operation, result should be null.
Here is my answer (T-SQL):
SELECT s.CustomerID,avg(s.TotalAmount) as AverageofLast3_operation
FROM (SELECT OrderID, CustomerID, EventDate, TotalAmount,
ROW_NUMBER() over (partition by CustomerID ORDER BY OrderID asc) as Row_num
FROM CustomerOperation
)s
WHERE s.Row_num>3
GROUP BY CustomerID
And the result is:
CustomerID
AverageofLast3_operation
1
7833
2
1966
According to the question, I should also have a row like this:
CustomerID
AverageofLast3_operation
3
NULL
How can I achieve this with T-SQL?
You need conditional aggregation:
SELECT CustomerID,
AVG(CASE WHEN counter >= 3 THEN TotalAmount END) AS AverageofLast3_operation
from (
SELECT OrderID, CustomerID, EventDate, TotalAmount,
ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderID DESC) AS Row_num,
COUNT(*) OVER (PARTITION BY CustomerID) counter
FROM CustomerOperation
) s
WHERE Row_num <= 3
GROUP BY CustomerID;
Or:
SELECT CustomerID,
CASE WHEN COUNT(*) = 3 THEN AVG(TotalAmount) END AS AverageofLast3_operation
from (
SELECT OrderID, CustomerID, EventDate, TotalAmount,
ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderID DESC) AS Row_num
FROM CustomerOperation
) s
WHERE Row_num <= 3
GROUP BY CustomerID;
See the demo.
You can use a conditional average like so:
with t as (
select customerId,
case when
Row_Number() over(partition by customerid order by orderid desc) <=3 then totalamount
else 0 end TotalAmount,
Count(*) over (partition by customerid) cnt
from CustomerOperation
)
select customerId, Avg(case when cnt>=3 then totalamount end) as Average
from t
where totalAmount>0
group by CustomerId

Get ID using join based on most recent date

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

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;

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

Find the Last Price that is different from the Current Price

I have sales transactions in a SQL Server table like this:
ItemNumber, TrxDate, UnitPrice
ABC, 1/1/2013, 10.00
ABC, 2/1/2013, 10.00
ABC, 3/1/2013, 13.00
ABC, 4/1/2013, 14.00
ABC, 5/1/2013, 14.00
XYZ, 1/1/2013, 18.00
XYZ, 2/1/2013, 18.00
XYZ, 3/1/2013, 20.00
XYZ, 4/1/2013, 20.00
XYZ, 5/1/2013, 20.00
I need a stored procedure to produce output that would look like this
ItemNumber, LastPrice, PriorPrice
ABC, 14.00, 13.00
XYZ, 20.00, 18.00
Assmunig SQL Server 2005+:
;WITH CTE AS
(
SELECT *,
RN=ROW_NUMBER() OVER(PARTITION BY ItemNumber ORDER BY TrxDate DESC)
FROM ( SELECT ItemNumber,
MAX(TrxDate) TrxDate,
UnitPrice
FROM YourTable
GROUP BY ItemNumber,
UnitPrice) A
)
SELECT ItemNumber,
MIN(CASE WHEN RN = 1 THEN UnitPrice END) LastPrice,
MIN(CASE WHEN RN = 2 THEN UnitPrice END) PriorPrice
FROM CTE
GROUP BY ItemNumber
You can do this using the lag() function first to find when the price changes:
select ItemNumber,
max(case when seqnum = 1 then Price end) as LastPrice,
max(case when seqnum = 2 then Price end) as PriorPrice
from (select t.*, row_number() over (partition by ItemNumber order by TrxDate desc) as seqnum
from (select t.*,
lag(Price) over (partition by ItemNumber order by TrxDate) as PrevPrice
from t
) t
where Price <> PrevPrice or PrevPrice is NULL
) t
group by ItemNumber;
Lag is only available starting with SQL Server 2012.
If you don't have lag(), you can do the same thing with a correlated subquery:
select ItemNumber,
max(case when seqnum = 1 then Price end) as LastPrice,
max(case when seqnum = 2 then Price end) as PriorPrice
from (select t.*, row_number() over (partition by ItemNumber order by TrxDate) as seqnum
from (select t.*,
(select top 1 t2.Price
from t t2
where t.ItemNumber = t2.ItemNumber and
t.TrxDate > t2.TrxDate
order by t2.TrxDate desc
) as PrevPrice
from t
) t
where Price <> PrevPrice or PrevPrice is NULL
) t
group by ItemNumber;