SQL group by with NULL - sql

I have a table something like this:
ID ProductID ProductName Price
== ========= =========== =====
1 XX1 TShirt 10
2 XX1 TShirt 10
3 NULL TShirt 10
4 XX2 Shirt 20
5 XX3 Shirt1 30
Now I want this to group by ProductName and results will be as follows
ID ProductID ProductName Price
== ========= =========== =====
1 XX1 TShirt 30
4 XX2 Shirt 20
5 XX3 Shirt1 30
Thanks

ProductID seems to be irrelevant for the group, so don't use it. To get all columns you could use a CTE and a ranking function like ROW_NUMBER:
WITH CTE AS(
SELECT ID,
ProductID,
ProductName,
Price = SUM(Price) OVER (PARTITION BY ProductName),
RN = ROW_NUMBER() OVER (PARTITION BY ProductName ORDER BY ID)
FROM dbo.TableName
)
SELECT CTE.* FROM CTE
WHERE RN = 1
If you want to take the row which contains the ProductID(where it is not NULL) modify the ORDER BY:
WITH CTE AS(
SELECT ID,
ProductID,
ProductName,
Price = SUM(Price) OVER (PARTITION BY ProductName),
RN = ROW_NUMBER() OVER (PARTITION BY ProductName
ORDER BY CASE WHEN ProductID IS NOT NULL
THEN 0 ELSE 1 END, ID)
FROM dbo.TableName
)
SELECT CTE.* FROM CTE
WHERE RN = 1

Related

SQL Server Find Max and Min in Group

I'm using Microsoft SQL Server and have a table like this:
DATE
ITEM
BUYER
QTY_BUY
2022-01-01
ITEM A
TOMMY
5
2022-01-01
ITEM A
BENNY
3
2022-01-01
ITEM A
ANDY
1
2022-01-01
ITEM A
JOHN
8
2022-01-01
ITEM B
TOMMY
2
2022-01-01
ITEM B
BENNY
10
2022-01-01
ITEM B
ANDY
3
2022-01-01
ITEM B
JOHN
6
2022-01-02
ITEM A
TOMMY
3
2022-01-02
ITEM A
BENNY
0
2022-01-02
ITEM A
ANDY
5
2022-01-02
ITEM A
JOHN
6
I want to show top buyer and min buyer group by date and item, so it will look like:
DATE
ITEM
TOP_BUYER
TOP_QTY
MIN_BUYER
QTY_MIN
2022-01-01
ITEM A
JOHN
8
ANDY
1
2022-01-01
ITEM B
BENNY
10
TOMMY
2
2022-01-02
ITEM A
JOHN
6
BENNY
0
Please help me to do that, I try so many trick but cannot reach it. Thanks in advance
We can handle this requirement using ROW_NUMBER:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY DATE, ITEM ORDER BY QTY_BUY) rn1,
ROW_NUMBER() OVER (PARTITION BY DATE, ITEM ORDER BY QTY_BUY DESC) rn2
FROM yourTable
)
SELECT DATE, ITEM,
MAX(CASE WHEN rn2 = 1 THEN BUYER END) AS TOP_BUYER,
MAX(CASE WHEN rn2 = 1 THEN QTY_BUY END) AS TOP_QTY,
MAX(CASE WHEN rn1 = 1 THEN BUYER END) AS MIN_BUYER,
MAX(CASE WHEN rn1 = 1 THEN QTY_BUY END) AS QTY_MIN
FROM cte
GROUP BY DATE, ITEM
ORDER BY DATE, ITEM;
The solution is to use first_value + partition over
This query was tested inside SQL Server
select distinct [date], Item
, FIRST_VALUE(buyer) OVER (partition by [date], item ORDER BY qty_buy desc) AS Top_Buyer
, FIRST_VALUE(qty_buy) OVER (partition by [date], item ORDER BY qty_buy desc) AS Top_Qty
, FIRST_VALUE(buyer) OVER (partition by [date], item ORDER BY [date], item, qty_buy asc) AS Min_Buyer
, FIRST_VALUE(qty_buy) OVER (partition by [date], item ORDER BY [date], item, qty_buy asc) AS Qty_Min
from testtable
It can also be done using simple group by and outer apply,
see this dbfiddle
select t.bdate,
t.item,
max(tb.buyer) as top_buyer,
max(t.qty) as top_qty,
max(mb.buyer) as min_buyer,
min(t.qty) as qty_min
from test t
outer apply ( select top 1 t2.buyer
from test t2
where t2.bdate = t.bdate
and t2.item = t.item
order by t2.qty desc
) tb
outer apply ( select top 1 t2.buyer
from test t2
where t2.bdate = t.bdate
and t2.item = t.item
order by t2.qty
) mb
group by t.bdate,
t.item
order by t.bdate

Merge or group rows corresponding to particular column postgresql

I want to group by the resultset further corresponding to the price_type column,
if the data for a product with both price_type variant and parent_product is present, then it must show only the variant one
For example, this data
Product Name PPID QTY PRID PRICE PRICE_TYPE
Shorts 1 10 21 200 variant
Shorts 1 10 21 100 parent_product
Night Suit 1 10 22 200 variant
Night Suit 1 10 22 100 parent_product
Denim 1 10 23 400 parent_product
should come like
Product Name PPID QTY PRID PRICE PRICE_TYPE
Shorts 1 10 21 200 variant
Night Suit 1 10 22 200 variant
Denim 1 10 23 400 parent_product
It seems you want row_number() with conditional ordering:
select *
from (select *, row_number() over (partition by ProductName
order by (case when Price_type = 'variant'
then 0 else 1
end)
) as seq
from table
) t
where seq = 1;
Below is the simple query to get desired result.
select
distinct on(product_name),
t.*
from tab t
order by price_type='variant' desc
You can use a window function:
SELECT * FROM
(
SELECT * ,
RANK() OVER (PARTITION BY product_name ORDER BY priority ) AS rank
FROM (
SELECT *,
CASE
WHEN price_type='variant' THEN 1
ELSE 2
END AS priority
FROM yourtable
) AS X
) AS Y
WHERE rank=1

How to Generate Row number Partition by two column match in sql

Tbl1
---------------------------------------------------------
Id Date Qty ReOrder
---------------------------------------------------------
1 1-1-18 1 3
2 2-1-18 0 3
3 3-1-18 2 3
4 4-1-18 3< >3
5 5-1-18 2 3
6 6-1-18 0 3
7 7-1-18 1 3
8 8-1-18 0 3
---------------------------------------------------------
I want the result like below
---------------------------------------------------------
Id Date Qty ReOrder
---------------------------------------------------------
1 1-1-18 1 3
5 5-1-18 2 3
---------------------------------------------------------
if ReOrder not same with Qty then date will be same upto after reorder=Qty
You can use cumulative approach with row_number() function :
select top (1) with ties *
from (select *, max(case when qty = reorder then 'v' end) over (order by id desc) grp
from table
) t
order by row_number() over(partition by grp order by id);
Unfortunately this will require SQL Server, But you can also do:
select *
from (select *, row_number() over(partition by grp order by id) seq
from (select *, max(case when qty = reorder then 'v' end) over (order by id desc) grp
from table
) t
) t
where seq = 1;

Update an "Order" column after deleting a row?

I have a ProductsImages table which includes ImageId, Productid ,Order :
ProductId ImageId Order
----------------------------
1 1 1
1 2 2
1 3 3
1 4 4
1 5 5
When I delete a row, let's say ProductId = 1 ImageId = 1 I need to update the order column :
ProductId ImageId Order
----------------------------
1 2 1
1 3 2
1 4 3
1 5 4
After you do the delete, you can do an update:
with toupdate as (
select pi.*,
row_number() over (partition by productId order by [order]) as neworder
from ProductImages pi
)
update toupdate
set [order] = neworder;
The above updates the whole table. If you just want to update the changed product, add a where clause:
with toupdate as (
select pi.*,
row_number() over (partition by productId order by [order]) as neworder
from ProductImages pi
where pi.ProductID = #ProductId
)
update toupdate
set [order] = neworder;

Group By by hiding a column - TSQL

I have a table structure
Table1
ID Hours Qty ProductID
1 2 1 100
1 3 5 200
2 6 6 100
2 2 2 200
If productid is (1,2,3) then i need sum ( Qty * Hours),If productid in (200,300,400,500) then i need sum(qty).
I have written a code like this
select ID,case when productid in (1,2,3) then
SUM( qty * hrs)
when productid in (100,200,300) then SUM( qty ) end result1
from Prod group by id ,productid
but i don't want to group by productid,i would like to pass it in "IN clause".How to achieve it.
Move the SUM() outside of the CASE WHEN statement.
SELECT
ID,
SUM(case when productid in (1,2,3) then qty * hrs
when productid in (100,200,300) then qty
end) result1
FROM
Prod
GROUP BY
ID
Assuming you want all columns plus the result of your query, you can do this:
select p.*, aux.result
from Prod p
inner join (select ID,case when productid in (1,2,3) then SUM( qty * hrs)
when productid in (100,200,300) then SUM( qty )
end as result
from Prod group by id ,productid) aux on aux.id = p.id