Row_Number function inside Case statement - sql

Need some help with the below logic:
I have an orders table with the following data:
Order_Id
created_date
Seller
001
01-01-2022
xx
001
02-01-2022
xx
001
03-01-2022
xx
001
04-01-2022
yy
I want the output as:
Order_Id
created_date
Seller
Ordinal_number
Reverse_Ordinal_number
001
01-01-2022
xx
1
3
001
02-01-2022
xx
2
2
001
03-01-2022
xx
3
1
001
04-01-2022
yy
0
0
I've tried the below SQL:
Select Order_Id , Created_date ,
Case when seller = 'xx' then row_number() over (partition by Order_Id Order by Created_Date ) else 0 end as Ordinal_number,
Case when seller = 'xx' then row_number() over (partition by Order_Id Order by Created_Date Desc) else 0 end as Reverse_Ordinal_number
from Orders
And I get the below output:
Order_Id
created_date
Seller
Ordinal_number
Reverse_Ordinal_number
001
01-01-2022
xx
1
4
001
02-01-2022
xx
2
3
001
03-01-2022
xx
3
2
001
04-01-2022
yy
0
0
Works fine when i do order by in Ascending order but not when i do it in descending order

According to the comments, the ordinal number should only be shown for the xx seller. However, the row_number call in your query doesn't take the seller into account. Add it to the partition by clause and you should be OK:
SELECT Order_Id,
Created_date,
CASE Seller WHEN 'xx'
THEN ROW_NUMBER() OVER (PARTITION BY Order_Id, Seller -- Here!
ORDER BY Created_Date)
ELSE 0 END AS Ordinal_number,
CASE Seller WHEN 'xx'
THEN ROW_NUMBER() OVER (PARTITION BY Order_Id, Seller -- And here!
ORDER BY Created_Date DESC)
ELSE 0 END AS Reverse_Ordinal_number,
FROM Orders

Related

How to calculate Cumulated count of Id from two different temp tables using same source table

My Table is like:
ID
Type
month
100
activate
01-11-2020
100
Paid
01-03-2021
100
Paid
01-06-2021
101
activate
01-03-2021
102
activate
01-04-2021
102
paid
01-04-2021
103
activate
01-04-2021
103
paid
01-06-2021
Now I have two temp tables:
tbl1 as (
Select *,case when row_number() over(partition by id order by month)=1 then 1 else null End as Cont1 from table where type='activate'),
tbl2 as (select *,,case when row_number() over(partition by id order by month)=1 then 1 else null End as Cont2 from table where type='paid')
select tbl1.id ,
tbl1.type,
tbl1.month as activate_month,
tbl2.month as paid_month,
-- datediff('month',tb1.month,tbl2.month) month_diff,
Sum(Cont1) over (partition by 1 order by tbl1.month rows unbounded preceding) as distA,
Sum(Cont2) over (partition by 1 order by tbl2.month rows unbounded preceding) as distP
from tbl1 ta
left join tbl2 tp on ta.id=tp.id
Scenario:
for type='activate' entry of id will exist only once but for type='paid' there might be multiple entries, so I want to count this id in cumulative order in such a way that we can consider the first entry in a count with a month.
Result I want:
ID
Type
Amonth
Pmonth
countA
countp
100
activate
01-11-2020
01-03-2021
1
1
101
activate
01-03-2021
null
2
0
102
activate
01-04-2021
01-04-2021
3
2
103
activate
01-04-2021
01-06-2021
4
3
You don't really need 2 CTE's for this.
An aggregation of a self-join will be sufficient.
You see, the window functions (f.e. row_number, dense_rank) are processed after aggregation. So you can use them also on a MIN.
For example:
select activate.id, activate.type
, min(activate.month) as activate_month
, min(paid.month) as activate_month
, row_number() over (order by min(activate.month) asc) as rn_activate
, case when min(paid.month) is not null
then row_number() over (order by min(paid.month) asc)
else 0
end as rn_paid
from yourtable as activate
left join yourtable as paid
on paid.id = activate.id
and paid.type = 'paid'
where activate.type = 'activate'
group by activate.id, activate.type
order by min(activate.month) asc;
id
type
activate_month
activate_month
rn_activate
rn_paid
100
activate
2020-11-01
2021-03-01
1
1
101
activate
2021-03-01
null
2
0
102
activate
2021-04-01
2021-04-01
3
2
103
activate
2021-04-01
2021-06-01
4
3
Demo on db<>fiddle here
If I understood correctly, this can be done using analytic functions with outer apply.
Select Tbl.ID, Tbl.Type, Min(Tbl.[month]) As Amonth, Min(T.Pmonth) As Pmonth,
Count(Tbl.ID) Over (Order by Min(Tbl.[month]) Rows Unbounded Preceding) As countA,
Case When T.ID Is Not Null
Then Count(T.ID) Over (Order by Min(T.Pmonth) Rows Unbounded Preceding)
Else 0
End As countp
From Tbl Outer Apply (Select ID, Min([month]) As Pmonth
From Tbl As T
Where ID=Tbl.ID
And Type='Paid'
Group by ID) As T
Where Tbl.Type='activate'
Group by Tbl.ID, T.ID, Tbl.Type
Order by Min(Tbl.[month])
Data output:
ID
Type
Amonth
Pmonth
countA
countp
100
activate
2020-11-01
2021-03-01
1
1
101
activate
2021-03-01
NULL
2
0
102
activate
2021-04-01
2021-04-01
3
2
103
activate
2021-04-01
2021-06-01
4
3

How to write a function to get the current effective date of a row in oracle

My Question goes farther then the question in the post below:
How to get the current effective date in Oracle?
I have some tables like the example in the post:
TID TName EffectiveDate
1 A 2011-7-1
2 A 2011-8-1
3 A 2011-9-1
4 A 2011-10-1
5 B 2011-8-1
6 B 2011-9-1
7 B 2011-10-1
8 C 2011-9-1
etc...
If today is 2011-9-10, I wish the query result will be like this:
TID TName EffectiveDate Status
1 A 2011-7-1 Invalid
2 A 2011-8-1 Invalid
3 A 2011-9-1 Valid
4 A 2011-10-1 Inactive
5 B 2011-8-1 Invalid
6 B 2011-9-1 Valid
7 B 2011-10-1 Inactive
8 C 2011-9-1 Valid
In the question above there was a solution like this:
select TID,
TName,
EffectiveDate,
decode(sign(EffectiveDate - (select max(T2.EffectiveDate)
from MyTable T2
where T1.Tname=T2.Tname and
T2.EffectiveDate <= sysdate)),
-1,'Invalid',
0,'Valid',
'Inactive') Status
from MyTable T1
and it works fine.
I wanted to make a function that you can call with a given table as parameter and that return you the status (valid, invalid or inactive) with the systemdate as default date value.
I don't think it is difficult.
My problem is that sometimes the table contains more columns that you have to take to determine your effective date.
In the example above you only have to look at the TName to take the current effectiveDate.
In the example below you have to take emplid and empl_rcd together
TID EMPLID EMPL_RCD EFFECTIVEDATE
1 001 0 2011-07-01
2 001 0 2011-06-25
3 001 1 2011-07-01
4 002 0 2011-07-01
5 002 0 2011-08-01
If today is 2012-01-01, the result has to be:
TID EMPLID EMPL_RCD EFFECTIVEDATE STATUS
1 001 0 2011-07-01 valid
2 001 0 2011-06-25 invalid
3 001 1 2011-07-01 valid
4 002 0 2011-07-01 invalid
5 002 0 2011-08-01 valid
In my second example my query should be like
select TID,
TName,
EffectiveDate,
decode(sign(EffectiveDate - (select max(T2.EffectiveDate)
from MyTable T2
where **T1.emplid=T2.emplid and
T1.empl_rcd = T2.empl_rcd and**
T2.EffectiveDate <= sysdate)),
-1,'Invalid',
0,'Valid',
'Inactive') Status
from MyTable T1
But I want a function that you can call like for example
select T1.*,effdt_status(mytable) as status from mytable
That return me the status of that row. But like a mention in the two different example, sometimes more columns has to be grouped to determine the effectiveDate.
Can anyone help me to write that function?
Is this what you are looking for?
select t.*,
(case sum(case when tdate <= date '2011-09-10' then 1 else 0 end) over (partition by tname order by tdate desc)
when 0 then 'Inactive'
when 1 then 'Valid'
else 'Invalid'
end)
from mytable t;
This should assign 'Inactive' to any future date, 'Valid' to the most recent date, and 'Invalid' to past ones.
You can also express this as:
select t.*,
(case when tdate > date '2011-09-10' then 'Inactive'
when not exists (select 1
from mytable t2
where t2.tname = t.tname and
t2.tdate > t.tdate and
t2.tdate <= date '2011-09-10'
)
then 'Valid'
else 'Invalid'
end)
from mytable t;

SQL help for table variable/CTE

Gurus,
I have a table which has log of all customer's shopping details as below. shopmode is a master table id.
ShopDate CustomerId ShoppingMode
1/1/2011 a 0
1/1/2011 a 0
1/1/2011 a 1
1/1/2011 b 0
2/1/2011 a 0
2/1/2011 b 1
3/1/2011 a 1
3/1/2011 b 0
3/1/2011 c 0
I am trying to comeup with query for requirements
(date is in dd/mm/yyyy)
Show one record per customer on shopdate,customerid,shopmode
1/1/2011 a 0
1/1/2011 a 1
1/1/2011 b 0
on a given date range(1/1/2011- 3/1/2011), need most recent shop date having unique value of customerid + shopmode
3/1/2011 a 1
3/1/2011 b 0
3/1/2011 c 0
2/1/2011 b 1
2/1/2011 a 0
on a given date range(1/1/2011-3/1/2011), the most recent by shopdate for customerid
3/1/2011 a 1
3/1/2011 b 0
3/1/2011 c 0
Need your help..
SELECT Max(shopdate),customerid, shopmode
FROM Table
with this result I will be joining shoppingdetail table to show data. Am tring to create a table variable or CTE show that I can join with other table.
1: just group by or select distinct
with cte1 as (
select ShopDate, CustomerId, ShoppingMode
from table
group by ShopDate, CustomerId, ShoppingMode)
select * from cte1;
2: First, find number of shopping types per day and customer. Then over those with one shopping type, just get the maximum date.
with cte2 as (
select ShopDate, CustomerId, max(ShoppingMode),
count(distinct ShoppingMode) as cnt
from table
where ShopDate between start_date and end_date
group by ShopDate, CustomerId
)
select max(ShopDate)as ShopDate, CustomerId, ShoppingMode
from cte2
where cnt = 1
group by Customer_id;
3: Just select all customers, rank them, and select what you want:
with cust as (
select
CustomerId,
row_number() over (partition by customerId order by ShopDate desc) as rnk
from table
where ShopDate between start_date and end_date
)
select * from cust where rnk = 1
Try this:
SELECT *
FROM (SELECT ShopDate,
CustomerId,
ShoppingMode,
ROW_NUMBER ()
OVER (PARTITION BY ShopDate, CustomerId, ShoppingMode
ORDER BY ShopDate, CustomerId, ShoppingMode)
rn
FROM yourtable where shopdate >= '01jan2011' AND shopdate <= '03jan2011')
WHERE rn = 1;

Oracle SQL : Pivot a column

I have table structure like below :
product supplier price qty
------- -------- ----- ---
SOAP ABC 50 10
SOAP DCE 50 10
BRUSH FGH 30 5
I would like to transform this table into :
product supplier_1 supplier_2 price qty
------- -------- ---------- ----- ---
SOAP ABC DCE 50 10
BRUSH FGH 30 5
how can I do it in SQL? thanks in advance.
Maybe this helps:
select product,
max(case when rn = 1 then supplier end) as supplier_1,
max(case when rn = 2 then supplier end) as supplier_2,
-- ...
-- max(case when rn = n then supplier end) as supplier_n,
avg(price) as price,
sum(qty) as sum
from(select t.*,
row_number() over (partition by product order by supplier) rn
from your_table t
)
group
by product;
select product,supplier_1,supplier_2,price,qty from table
model
return all rows
dimension by(product)
measures(supplier,
lpad(' ',10) supplier_1,
lpad(' ',10) supplier_2
)
rules upsert(
supplier_1[0] = supplier['SOAP'],
supplier_2[0] = supplier['BRUSH']
)
10G or 10G above available.

SQL Query to find the most recent group of records

I have a history of records (multiple records per update all with the exact same datetime) that share an IdString.
I want a query to determine which of these records are part of the most recent update group.
This query will show me one of the records having the most recent update date, but for each partition, I need all the records with that max date.
;with cte as(
select ROW_NUMBER() over (partition by IdString order by UpdateDate desc) as [rn], *
from MyTable
)
select CASE WHEN (cte.rn = 1) THEN 0 ELSE 1 END [IsOld], *
from MyTable m
inner join cte on cte.RecordId= m.RecordId
Would someone please help me figure out an appropriate query?
EDIT: Sample
(IsOld is the desired calculated value)
IsOld RecordId IdString UpdateDate
1 1 ABC 2011-06-16
1 2 ABC 2012-05-30
1 3 ABC 2008-12-31
0 4 ABC 2012-06-08
1 5 ABC 2011-01-16
0 6 ABC 2012-06-08
1 7 ABC 2012-06-07
1 8 XYZ 2001-01-16
1 9 XYZ 2013-01-30
0 10 XYZ 2001-01-31
1 11 XYZ 2013-06-01
1 12 XYZ 2001-05-04
0 13 XYZ 2013-01-30
SELECT CASE WHEN updateDate = maxDate THEN 0 ELSE 1 END isOldRecord, RecordID, IDString, UpdateDate
FROM
(
select m.RecordID, m.IDString, m.updateDate, MAX(UpdateDate) OVER (PARTITION BY IDString) maxDate
from MyTable m
) A
Try this -
;WITH cte AS(
SELECT RANK() OVER(PARTITION BY IdString ORDER BY UpdateDate DESC) AS [row_num], *
FROM MyTable
)
SELECT CASE WHEN m.[row_num] = 1 THEN 0 ELSE 1 END isOld, *
from cte m