increasing value with condition on oracle - sql

How to add an increment value (not summarize) with some condition on another column?
I'm using Oracle-like DBMS, named Tibero, for simple example i want to produce this data
ROWNUM GRP_STRT GRP_NO SLBY
1 1 1 1
2 1 1 1
3 1 1 1
4 1 1 1
5 1 1 1
6 1 2 0
7 1 2 0
8 1 3 1
9 1 3 1
10 1 3 1
11 1 4 0
12 1 5 1
Column SLBY is for Buy/Sell code (0=Buy, 1=Sell) then every changing type of transaction, column GRP_NO increasing (but it's not grouping by SLBY column)
SELECT CASE
WHEN ROWNUM = 1 THEN GRP_NO
WHEN ROWNUM <> 1 AND SLBY = LAG(SLBY,1) over (ORDER BY ROWNUM) THEN LAG(GRP_STRT,1) over (ORDER BY ROWNUM) - 1
WHEN ROWNUM <> 1 AND SLBY_DSTN_CD <> LAG(SLBY_DSTN_CD,1) over (ORDER BY ROWNUM) THEN LAG(GRP_STRT,1) over (ORDER BY ROWNUM) + 1
END TARGET_GROUPING
, A.*
FROM SOME_TABLE
I tried with that query but instead of getting what i want like in the picture above, I produced a GRP_NO like 1 1 1 1 1 2 1 1 1 1 2 1 1 1 (first change SLBY only)
Apologies for my bad english and bad explanation, I'll explain more if need further information, thanks for your help!

As far as I understood your problem,
You are trying to calculate GRP_NO from ROWNUM, GRP_STRT, GRP_NO, and SLBY.
I have created the following query for you.
You can check the logic and apply it in your code accordingly:
SELECT
RN,
GRP_STRT,
SUM(CASE
WHEN PREV_SLBY_DSTN_CD IS NULL
OR PREV_SLBY_DSTN_CD <> SLBY_DSTN_CD THEN 1
END) OVER(
ORDER BY
RN
) AS GRP_NO,
SLBY_DSTN_CD AS SLBY
FROM
(
SELECT
RN,
LAG(SLBY_DSTN_CD) OVER(
ORDER BY
RN
) AS PREV_SLBY_DSTN_CD,
SLBY_DSTN_CD,
GRP_STRT
FROM
(SELECT ROWNUM RN, .... FROM SOME_TABLE) A
)
This code is to generate the output as shown in question:
ROWNUM GRP_STRT GRP_NO SLBY
1 1 1 1
2 1 1 1
3 1 1 1
4 1 1 1
5 1 1 1
6 1 2 0
7 1 2 0
8 1 3 1
9 1 3 1
10 1 3 1
11 1 4 0
12 1 5 1
Cheers!!

Related

Select top rows until value in specific column has appeared twice

I have the following query where I am trying to select all records, ordered by date, until the second time EmailApproved = 1 is found. The second record where EmailApproved = 1 should not be selected.
declare #Test table (id int, EmailApproved bit, Created datetime);
insert into #Test (id, EmailApproved, Created)
values
(1,0,'2011-03-07 03:58:58.423')
, (2,0,'2011-02-21 04:55:52.103')
, (3,0,'2011-01-29 13:24:02.103')
, (4,1,'2010-10-12 14:41:54.217')
, (5,0,'2010-10-12 14:34:15.903')
, (6,0,'2010-10-12 10:10:19.123')
, (7,1,'2010-08-27 12:07:16.073')
, (8,1,'2010-08-25 12:15:49.413')
, (9,0,'2010-08-25 12:14:51.970')
, (10,1,'2010-04-12 16:43:44.777');
select *
, case when Row1 = Row2 then 1 else 0 end Row1EqualRow2
from (
select id, EmailApproved, Created
, row_number() over (partition by EmailApproved order by Created desc) Row1
, row_number() over (order by Created desc) Row2
from #Test
) X
--where Row1 = Row2
order by Created desc;
Which produces the following results:
id EmailApproved Created Row1 Row2 Row1EqualsRow2
1 0 2011-03-07 03:58:58.423 1 1 1
2 0 2011-02-21 04:55:52.103 2 2 1
3 0 2011-01-29 13:24:02.103 3 3 1
4 1 2010-10-12 14:41:54.217 1 4 0
5 0 2010-10-12 14:34:15.903 4 5 0
6 0 2010-10-12 10:10:19.123 5 6 0
7 1 2010-08-27 12:07:16.073 2 7 0
8 1 2010-08-25 12:15:49.413 3 8 0
9 0 2010-08-25 12:14:51.970 6 9 0
10 1 2010-04-12 16:43:44.777 4 10 0
What I actually want is:
id EmailApproved Created Row1 Row2 Row1EqualsRow2
1 0 2011-03-07 03:58:58.423 1 1 1
2 0 2011-02-21 04:55:52.103 2 2 1
3 0 2011-01-29 13:24:02.103 3 3 1
4 1 2010-10-12 14:41:54.217 1 4 0
5 0 2010-10-12 14:34:15.903 4 5 0
6 0 2010-10-12 10:10:19.123 5 6 0
Note: Row, Row2 & Row1EqualsRow2 are just working columns to show my calculations.
Steps:
Create a row number, rn, over all rows in case id is not in sequence.
Create a row number, approv_rn, partitioned by EmailApproved so we know when EmailApproved = 1 for the second time
Use a outer apply to find the row number of the second instance of EmailApproved = 1
In the where clause filter out all rows where the row number is >= the value found in step 3.
If there is 1 or 0 EmailApproved records available then the outer apply will return null, in which case return all available rows.
with test as
(
select *,
rn = row_number() over (order by Created desc),
approv_rn = row_number() over (partition by EmailApproved
order by Created desc)
from #Test
)
select *
from test t
outer apply
(
select x.rn
from test x
where x.EmailApproved = 1
and x.approv_rn = 2
) x
where t.rn < x.rn or x.rn is null
order by t.Created desc;

Can I start a new group when value changes from 0 to 1?

Can I somehow assign a new group to a row when a value in a column changes in T-SQL?
I would be grateful if you can provide solution that will work on unlimited repeating numbers without CTE and functions. I made a solution that work in sutuation with 100 consecutive identical numbers(with
coalesce(lag()over(), lag() over(), lag() over() ) - it is too bulky
but can not make a solution for a case with unlimited number of consecutive identical numbers.
Data
id somevalue
1 0
2 1
3 1
4 0
5 0
6 1
7 1
8 1
9 0
10 0
11 1
12 0
13 1
14 1
15 0
16 0
Expected
id somevalue group
1 0 1
2 1 2
3 1 2
4 0 3
5 0 3
6 1 4
7 1 4
8 1 4
9 0 5
10 0 5
11 1 6
12 0 7
13 1 8
14 1 8
15 0 9
16 0 9
If you just want a group identifier, you can use:
select t.*,
min(id) over (partition by some_value, seqnum - seqnum_1) as grp
from (select t.*,
row_number() over (order by id) as seqnum,
row_number() over (partition by somevalue order by id) as sequm_1
from t
) t;
If you want them enumerated . . . well, you can enumerate the id above using dense_rank(). Or you can use lag() and a cumulative sum:
select t.*,
sum(case when some_value = prev_sv then 0 else 1 end) over (order by id) as grp
from (select t.*,
lag(somevalue) over (order by id) as prev_sv
from t
) t;
Here's a different approach:
First I created a view to provide the group increment on each row:
create view increments as
select
n2.id,n2.somevalue,
case when n1.somevalue=n2.somevalue then 0 else 1 end as increment
from
(select 0 as id,1 as somevalue union all select * from mytable) n1
join mytable n2
on n2.id = n1.id+1
Then I used this view to produce the group values as cumulative sums of the increments:
select id, somevalue,
(select sum(increment) from increments i1 where i1.id <= i2.id)
from increments i2

Select first date in which an event happen for each id

I have a series of Ids, some of them activate a product on certain month and that product remains activated for an X period of time, while others do not activate the product.
I want to create a column which indicates in which month the user activates the product or a NULL if the user doesn't activate it.
I've tried using a partition like the following:
SELECT id, fl_testdrive, month_dt,
CASE WHEN fl_testdrive = 1 then min(month_dt) OVER(PARTITION BY id ORDER BY month_dt ROWS UNBOUNDED PRECEDING) else 0 end as month_testdrive
FROM Table_1
However, when I try this solution, in the column month_testdrive, I do not obtain the first month in which the user appears, indepently of if he/she activated that product in that month or on a later one.
This is what I get with my query
Id flag_testdrive month_dt month_testdrive
1 0 1 1
1 0 2 1
1 1 3 1
1 1 4 1
2 0 2 0
2 0 3 0
3 1 4 4
3 1 5 4
What I'd expect:
Id flag_testdrive month_dt month_testdrive
1 0 1 3
1 0 2 3
1 1 3 3
1 1 4 3
2 0 2 0
2 0 3 0
3 1 4 4
3 1 5 4
This solution is a second best but is also fine:
Id flag_testdrive month_dt month_testdrive
1 0 1 0
1 0 2 0
1 1 3 3
1 1 4 3
2 0 2 0
2 0 3 0
3 1 4 4
3 1 5 4
You want CASE expression inside MIN():
MIN(CASE WHEN fl_testdrive = 1 THEN month_dt ELSE 0 END) OVER(PARTITION BY id, flag_testdrive ORDER BY month_dt ROWS UNBOUNDED PRECEDING)
Here's an option for you:
DECLARE #Testdate TABLE(
id INT
,flag_testdrive INT
,month_dt INT
)
INSERT INTO #Testdate (
[id]
, [flag_testdrive]
, [month_dt]
)
VALUES(1,0,1)
,(1,0,2)
,(1,1,3)
,(1,1,4)
,(2,0,2)
,(2,0,3)
,(3,1,4)
,(3,1,5)
SELECT
*
,COALESCE((SELECT MIN([aa].[month_dt]) FROM #Testdate aa
WHERE aa.[id] = a.id
AND aa.[flag_testdrive] = 1), 0) AS month_testdrive
FROM #Testdate a
Return the minimum month_dt for a given id only if flag_testdrive=1, wrapped in coalesce to return 0 instead of NULL.

Postgres query which it replys number which is last different value

I want to query which replys last different values row number from current row.
NUMBER takes only 2 value.
Table A is given
ROWNUM NUMBER
1 1
2 1
3 1
4 1
5 -1
6 -1
7 1
8 1
9 -1
10 -1
11 -1
EXPECTED result FROM Table A by some query.
ROWNUM NUMBER LASTDIFFERENT
1 1 5
2 1 4
3 1 3
4 1 2
5 -1 3
6 -1 2
7 1 3
8 1 2
9 -1
10 -1
11 -1
This might fall into the category of "just because you can doesn't mean you should." I don't see any elegant solutions to your problem, but this is a working solution, at least for your sample data:
with switches as(
select
rownum, number,
case
when lag(number) over (order by rownum) = number then 0
else 1
end switch
from TableA
),
groups as (
select
rownum, number, sum (switch) over (order by rownum) group_id
from switches
)
select
rownum, number, -- group_id,
max (rownum) over (partition by group_id) - rownum + 2 as last_different
from groups
I ran this on your sample data and got these results:
rownum number last_different
1 1 5
2 1 4
3 1 3
4 1 2
5 -1 3
6 -1 2
7 1 3
8 1 2
9 -1 4
10 -1 3
11 -1 2

T-SQL Reverse Pivot on every character of a string

We have a table like below in an sql server 2005 db:
event_id staff_id weeks
1 1 NNNYYYYNNYYY
1 2 YYYNNNYYYNNN
2 1 YYYYYYYYNYYY
This is from a piece of timetabling software and is basically saying which staff members are assigned to an event (register) and the set of weeks they are teaching that register. So staff_id 1 isn't teaching the first 3 weeks of event 1 but is teaching the following 4....
Is there an easy way to convert that to an easier form such as:
event_id staff_id week
1 1 4
1 1 5
1 1 6
1 1 7
1 1 10
1 1 11
1 1 12
1 2 1
1 2 2
1 2 3
1 2 7
1 2 8
1 2 9
2 1 1
2 1 2
2 1 3
2 1 4
2 1 5
2 1 6
2 1 7
2 1 8
2 1 10
2 1 11
2 1 12
WITH cte AS
(
SELECT 1 AS [week]
UNION ALL
SELECT [week] + 1
FROM cte
WHERE [week] < 53
)
SELECT t.event_id, t.staff_id, cte.[week]
FROM your_table AS t
INNER JOIN cte
ON LEN(ISNULL(t.weeks, '')) >= cte.[week]
AND SUBSTRING(t.weeks, cte.[week], 1) = 'Y'
ORDER BY t.event_id, t.staff_id, cte.[week]