Finding adjacent column values from the last non-null value of a certain column in Snowflake (SQL) using partition by - sql

Say I have the following table:
ID
T
R
1
2
1
3
Y
1
4
1
5
1
6
Y
1
7
I would like to add a column which equals the value from column T based on the last non-null value from column R. This means the following:
ID
T
R
GOAL
1
2
1
3
Y
1
4
Y
3
1
5
4
1
6
Y
4
1
7
6
I do have many ID's so I need to make use of the OVER (PARTITION BY ...) clause. Also, if possible, I would like to use a single statement, like
SELECT *
, GOAL
FROM TABLE
So without any extra select statement.

T is in ascending order so just null it out according to R and take the maximum looking backward.
select *,
max(case when R is not null then T end)
over (
partition by id
order by T
rows between unbounded preceding and 1 preceding
) as GOAL
from TBL
http://sqlfiddle.com/#!18/c927a5/5

Related

SQL Get max value of n next rows

Say I have a table with two columns: the time and the value. I want to be able to get a table with :
for each time get the max values of every next n seconds.
If I want the max value of every next 3 seconds, the following table:
time
value
1
6
2
1
3
4
4
2
5
5
6
1
7
1
8
3
9
7
Should return:
time
value
max
1
6
6
2
1
4
3
4
5
4
2
5
5
5
5
6
1
3
7
1
7
8
3
NULL
9
7
NULL
Is there a way to do this directly with an sql query?
You can use the max window function:
select *,
case
when row_number() over(order by time desc) > 2 then
max(value) over(order by time rows between current row and 2 following)
end as max
from table_name;
Fiddle
The case expression checks that there are more than 2 rows after the current row to calculate the max, otherwise null is returned (for the last 2 rows ordered by time).
Similar Version to Zakaria, but this solution uses about 40% less CPU resources (scaled to 3M rows for benchmark) as the window functions both use the same exact OVER clause so SQL can better optimize the query.
Optimized Max Value of Rolling Window of 3 Rows
SELECT *,
MaxValueIn3SecondWindow = CASE
/*Check 3 rows exists to compare. If 3 rows exists, then calculate max value*/
WHEN 3 = COUNT(*) OVER (ORDER BY [Time] ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)
/*Returns max [Value] between the current row and the next 2 rows*/
THEN MAX(A.[Value]) OVER (ORDER BY [Time] ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)
END
FROM #YourTable AS A

Row Number with specific window size

I want to group records by row numbers.
Like from row 1-3 in group 1 , 4-6 in group 2 , 7-9 in group 3 and so on.
Suppose below is the table structure:
Row NumberDataValue
1 A 10
2 A 5
3 A 1
4 A 33
5 A 2
6 A 127
1 B 1
2 B 0
3 B 7
4 B 7
5 B 5
6 B 8
7 B 1
8 B 0
I want a output like this:
GroupValue
1 10
1 5
1 1
2 33
2 2
2 127
1 1
1 0
1 7
2 7
2 5
2 8
3 1
3 0
I am using Oracle 11G.
I can achieve this using PL/SQL. But I have to use SQL only. As I have to use this query in a reporting tool.
If this is a duplicate question please provide the link of the answered question.
Subtract 1 from the column "RowNumber" and divide by 3.
Then use TRUNC() to get the integer part:
SELECT TRUNC(("RowNumber" - 1) / 3) + 1 "Group",
"Value"
FROM tablename
See the demo.
I would assume the name of the first column is ordering.
You can do:
select
1 + trunc(row_number() over(partition by data order by ordering) - 1) / 3,
value
from t
What you show looks like the output from something like this:
select ceil(rn/3) as grp, value
from your_table
order by rn;
Note that "row number" and "group" are reserved words/phrases which should not be used as column names. I used rn and grp instead.
I think the ceiling function is the simplest way to arrive at what you want. If you want to base it on the RowNumber column:
select ceil( RowNumber / 3.0) as grouping
If you want to calculate it yourself using row_number():
select ceil( row_number() over (order by RowNumber) / 3.0 ) as grouping

Can you rearrange rows in sql tables accodring to a custom logic?

So I've been trying to change the order of rows according to my own logic.
Let's say I have a set of numbers from 1 to 4. If the rows have n entries of different values from 1 to 4 and I want to rearrange all the rows such that I have only rows with 4 at the top followed by a grouped combination of two 2's and a 1, followed by the 3's.
How can it be done using Sql server?
ID ROWS --> ID NEW ORDER
-- ---- -- ---------
A 1 G 4
B 1 G 4
C 2 G 4
C 2 G 4
D 2 C 2
D 2 C 2
E 2 A 1
E 2 E 2
F 3 E 2
F 3 B 1
F 3 D 2
G 4 D 2
G 4 F 3
G 4 F 3
G 4 F 3
Code till now:
SELECT * FROM table
ORDER BY
CASE
WHEN ROW = 4 THEN '1'
WHEN ROW = 1 THEN '2'
WHEN ROW = 2 THEN '3'
WHEN ROW = 3 THEN '4'
END ASC
Assuming ROWS is exactly the number of rows with a paticular ID,
order the rows first by a group according to ROWS interval (4),(1..2), (all the rest) then within the second group row_number() sequences of IDs having ROWS = 1 and 2 independently. Within the same sequence number order by ROWS reverse order.
select ID, ROWS
from (
select *
, case when ROWS = 4 then 1
when ROWS between 1 and 2 then 2
else 3 end grp1
, row_number() over(partition by ROWS order by ROWS) - row_number() over(partition by ROWS, ID order by ID) pos2
from yourtable) t
order by grp1, case ROWS when 1 then pos2 else pos2/2 end, -ROWS, ID
If you want you can try doing it using order by clause. You can use your logic to sort values accordingly. Default order by ASC. So i have tried below query with order by if this gives you some idea of doing it:
select id from test order by case when id%2 = 0 then id end desc
Will give;
ID
8
6
4
5
So you can try ordering with some logic inside.

Fast inclusion of the previous value of another type

I have a table of the following structure:
Ordinal Type
1 A
2 B
3 A
4 B
5 B
6 B
7 A
There are two types and the order according to the ordinal matters. I want the following result:
Ordinal Type Last_A
1 A 1
2 B 1
3 A 3
4 B 3
5 B 3
6 B 3
7 A 7
The new column Last_A should contain the last seen Ordinal for which the Type = A, where last is relative to the order of the Ordinal. There may be an arbitrary number of B-rows before another A-row. Is there a performance efficient way of achieving this result? Using a cursor would easily achieve the desired result, but is not feasible due to the large amount of rows I work with.
You can use a conditional cumulative max():
select t.*,
max(case when t.type = 'A' then ordinal end) over (order by ordinal) as last_A
from t;

Delete rows, which are duplicated and follow each other consequently

It's hard to formulate, so i'll just show an example and you are welcome to edit my question and title.
Suppose, i have a table
flag id value datetime
0 b 1 343 13
1 a 1 23 12
2 b 1 21 11
3 b 1 32 10
4 c 2 43 11
5 d 2 43 10
6 d 2 32 9
7 c 2 1 8
For each id i want to squeze the table by flag columns such that all duplicate flag values that follow each other collapse to one row with sum aggregation. Desired result:
flag id value
0 b 1 343
1 a 1 23
2 b 1 53
3 c 2 75
4 d 2 32
5 c 2 1
P.S: I found functions like CONDITIONAL_CHANGE_EVENT, which seem to be able to do that, but the examples of them in docs dont work for me
Use the differnece of row number approach to assign groups based on consecutive row flags being the same. Thereafter use a running sum.
select distinct id,flag,sum(value) over(partition by id,grp) as finalvalue
from (
select t.*,row_number() over(partition by id order by datetime)-row_number() over(partition by id,flag order by datetime) as grp
from tbl t
) t
Here's an approach which uses CONDITIONAL_CHANGE_EVENT:
select
flag,
id,
sum(value) value
from (
select
conditional_change_event(flag) over (order by datetime desc) part,
flag,
id,
value
from so
) t
group by part, flag, id
order by part;
The result is different from your desired result stated in the question because of order by datetime. Adding a separate column for the row number and sorting on that gives the correct result.