How to update rows based on a shared ID within a single table - sql

Currently I have a table that looks like below:
ID|Date |Val1|Val2|
1 |1/1/2016|1000|0
2 |1/1/2016|Null|0
3 |1/1/2016|Null|0
1 |2/1/2016|1000|0
2 |2/1/2016|Null|0
3 |2/1/2016|1000|0
1 |3/1/2016|1000|0
2 |3/1/2016|1000|0
3 |3/1/2016|1000|0
I want val2 to become 1 if Val1 is populated in the previous month, so the output would look like:
ID|Date |Val1|Val2|
1 |1/1/2016|1000|0
2 |1/1/2016|Null|0
3 |1/1/2016|Null|0
1 |2/1/2016|1000|1
2 |2/1/2016|Null|0
3 |2/1/2016|1000|0
1 |3/1/2016|1000|1
2 |3/1/2016|1000|0
3 |3/1/2016|1000|1
I've tried a few code combinations, but the conditional of updating the value by the previous date where Val1 first appears is tripping me up. I'd appreciate any help!

You can do this with a windowed LAG() to find the previous value, and update Val2 if it's NOT NULL.
;With Cte As
(
Select Id, [Date-----], Val1, Val2,
Lag(Val1) Over (Partition By Id Order By [Date-----] Asc) As Prev
From LikeBelow
)
Update Cte
Set Val2 = 1
Where Prev Is Not Null;
If you are actually storing your dates as a VARCHAR and not a DATE, you'll need to convert it:
;With Cte As
(
Select Id, [Date-----], Val1, Val2,
Lag(Val1) Over (Partition By Id
Order By Convert(Date, [Date-----]) Asc) As Prev
From LikeBelow
)
Update Cte
Set Val2 = 1
Where Prev Is Not Null;

Related

Group by and aggregation on Bigquery

I have a table which has the following format (Google Big query) :
user
url
val1
val2
val3
...
val300
A
a
0.5
0
-3
...
1
A
b
1
2
3
...
2
B
c
5
4
-10
...
2
I would like to obtain a new table where I obtain the number of urls by user, and vals are aggregated by average. (The number of different vals can be variable so I would like to have something rather flexible)
user
nb_url
val1
val2
val3
...
val300
A
2
0.75
1
0
...
1.5
B
1
...
What is the good syntax?
Thank you in advance
Aggregate by user, select the count of URLs, and the average of the other columns.
SELECT
user,
COUNT(*) AS nb_url,
AVG(val1) AS val1,
AVG(val2) AS val2,
AVG(val3) AS val3,
...
AVG(val300) AS val300
FROM yourTable
GROUP BY user
ORDER BY user;
Generating pivot for 300 columns can be quite expensive even for BigQuery - instead I would recommend below [unpivoted] solution
select user, count(url) nb_url,
offset + 1 col, avg(cast(val as float64)) as val
from your_table t,
unnest(split(translate(format('%t', (select as struct * except(user, url) from unnest([t]))), '() ', ''))) val with offset
group by user, col
if applied to sample data as in your question - output is

Row_number not showing 1 rank

I am using the below code to get the rank but not able to see the 1st rank. I am getting the results 2 and 3.
Select BPMDate,Tenor,TenorStartDays,TenorEndDays,NominalTransacted,
SumofNominalRate,AverageRate,VWAR,rw,SortOrder
FROM
(
SELECT case_id,created_date BPMDate,tenor Tenor,tenor_start_days TenorStartDays,
tenor_end_days TenorEndDays
,nominal_transacted NominalTransacted,sum_of_nominal_rates SumofNominalRate
,average_rate AverageRate,vmar VWAR
,Case when tenor='O/N' Then 1 when tenor='1W' Then 2 when tenor='1M' Then 3
when tenor='3M' Then 4
when tenor='6M' Then 5 when tenor='1Y' Then 6 Else 7 End SortOrder,
row_number() over ( partition by tenor order by created_date desc ) rw
From table1
Where df_type = 'DF1' and to_date(created_date) >= '2020-10-13' and
to_date(created_date) <= '2020-10-13'
) ard
inner join
(
select case_id,case_status from table2
where case_status='Completed') ad
on ard.case_id=ad.case_id
order by sortorder
The column rw is beginning from 2 instead 1
thanks for the support
I think you are filtering it out when you join to ad , If you just select the sub query I think you will be able to see it.

SQL - Group by numbers according to their difference

I have a table and I want to group rows that have at most x difference at col2.
For example,
col1 col2
abg 3
abw 4
abc 5
abd 6
abe 20
abf 21
After query I want to get groups such that
group 1: abg 3
abw 4
abc 5
abd 6
group 2: abe 20
abf 21
In this example difference is 1.
How can write such a query?
For Oracle (or anything that supports window functions) this will work:
select col1, col2, sum(group_gen) over (order by col2) as grp
from (
select col1, col2,
case when col2 - lag(col2) over (order by col2) > 1 then 1 else 0 end as group_gen
from some_table
)
Check it on SQLFiddle.
This should get what you need, and changing the gap to that of 5, or any other number is a single change at the #lastVal +1 (vs whatever other difference). The prequery "PreSorted" is required to make sure the data is being processed sequentially so you don't get out-of-order entries.
As each current row is processed, it's column 2 value is stored in the #lastVal for test comparison of the next row, but remains as a valid column "Col2". There is no "group by" as you are just wanting a column to identify where each group is associated vs any aggregation.
select
#grp := if( PreSorted.col2 > #lastVal +1, #grp +1, #grp ) as GapGroup,
PreSorted.col1,
#lastVal := PreSorted.col2 as Col2
from
( select
YT.col1,
YT.col2
from
YourTable YT
order by
YT.col2 ) PreSorted,
( select #grp := 1,
#lastVal := -1 ) sqlvars
try this query, you can use 1 and 2 as input and get you groups:
var grp number(5)
exec :grp :=1
select * from YourTABLE
where (:grp = 1 and col2 < 20) or (:grp = 2 and col2 > 6);

how to normalize / update a "order" column

i have a table "mydata" with some data data :
id name position
===========================
4 foo -3
6 bar -2
1 baz -1
3 knork -1
5 lift 0
2 pitcher 0
i fetch the table ordered using order by position ASC;
the position column value may be non unique (for some reason not described here :-) and is used to provide a custom order during SELECT.
what i want to do :
i want to normalize the table column "position" by associating a unique position to each row which doesnt destroy the order. furthermore the highest position after normalising should be -1.
wished resulting table contents :
id name position
===========================
4 foo -6
6 bar -5
1 baz -4
3 knork -3
5 lift -2
2 pitcher -1
i tried several ways but failed to implement the correct update statement.
i guess that using
generate_series( -(select count(*) from mydata), -1)
is a good starting point to get the new values for the position column but i have no clue how to merge that generated column data into the update statement.
hope somebody can help me out :-)
Something like:
with renumber as (
select id,
-1 * row_number() over (order by position desc, id) as rn
from foo
)
update foo
set position = r.rn
from renumber r
where foo.id = r.id
and position <> r.rn;
SQLFiddle Demo
Try this one -
Query:
CREATE TABLE temp
(
id INT
, name VARCHAR(10)
, position INT
)
INSERT INTO temp (id, name, position)
VALUES
(4, 'foo', -3),
(6, 'bar', -2),
(1, 'baz', -1),
(3, 'knork', -1),
(5, 'lift', 0),
(2, 'pitcher', 0)
SELECT
id
, name
, position = -ROW_NUMBER() OVER (ORDER BY position DESC, id)
FROM temp
ORDER BY position
Update:
UPDATE temp
SET position = t.rn
FROM (
SELECT id, rn = - ROW_NUMBER() OVER (ORDER BY position DESC, id)
FROM temp
) t
WHERE temp.id = t.id
Output:
id name position
----------- ---------- --------------------
4 foo -6
6 bar -5
3 knork -4
1 baz -3
5 lift -2
2 pitcher -1
#a_horse_with_no_name is really near the truth - thank you !
UPDATE temp
SET position=t.rn
FROM (SELECT
id, name,
-((select count( *)
FROM temp)
+1-row_number() OVER (ORDER BY position ASC)) as rn
FROM temp) t
WHERE temp.id=t.id;
SELECT * FROM temp ORDER BY position ASC;
see http://sqlfiddle.com/#!1/d1770/6
update mydata temp1, (select a.*,#var:=#var-1 sno from mydata a, (select #var:=0) b
order by position desc, id asc) temp2
set temp1.position = temp2.sno
where temp1.id = temp2.id;

Oracle SQL -- select from two columns and combine into one

I have this table:
Vals
Val1 Val2 Score
A B 1
C 2
D 3
I would like the output to be a single column that is the "superset" of the Vals1 and Val2 variable. It also keeps the "score" variable associated with that value.
The output should be:
Val Score
A 1
B 1
C 2
D 3
Selecting from this table twice and then unioning is absolutely not a possibility because producing it is very expensive. In addition I cannot use a with clause because this query uses one in a sub-query and for some reason Oracle doesn't support two with clauses.
I don't really care about how repeat values are dealt with, whatever is easiest/fastest.
How can I generate my appropriate output?
Here is solution without using unpivot.
with columns as (
select level as colNum from dual connect by level <= 2
),
results as (
select case colNum
when 1 then Val1
when 2 then Val2
end Val,
score
from vals,
columns
)
select * from results where val is not null
Here is essentially the same query without the WITH clause:
select case colNum
when 1 then Val1
when 2 then Val2
end Val,
score
from vals,
(select level as colNum from dual connect by level <= 2) columns
where case colNum
when 1 then Val1
when 2 then Val2
end is not null
Or a bit more concisely
select *
from ( select case colNum
when 1 then Val1
when 2 then Val2
end Val,
score
from vals,
(select level as colNum from dual connect by level <= 2) columns
) results
where val is not null
try this, looks like you want to convert column values into rows
select val1, score from vals where val1 is not null
union
select val2,score from vals where val2 is not null
If you're on Oracle 11, unPivot will help:
SELECT *
FROM vals
UNPIVOT ( val FOR origin IN (val1, val2) )
you can choose any names instead of 'val' and 'origin'.
See Oracle article on pivot / unPivot.