Oracle: Get the smaller values and the first greater value - sql

I have a table like this;
ID Name Value
1 Sample1 10
2 Sample2 20
3 Sample3 30
4 Sample4 40
And I would like to get all of the rows that contain smaller values and the first row that contains greater value.
For example when I send '25' as a parameter to Value column, I want to have following table;
ID Name Value
1 Sample1 10
2 Sample2 20
3 Sample3 30
I'm stuck at this point, thanks in advance.

Analytic functions to the rescue!
create table your_table (
id number,
value number)
insert into your_table
select level, level * 10
from dual
connect by level <= 5
select * from your_table
id | value
----+------
1 | 10
2 | 20
3 | 30
4 | 40
5 | 50
Ok, now we use lag(). Specify field, offset and the default value (for the first row that has no previous one).
select id, value, lag(value, 1, value) over (order by value) previous_value
from your_table
id | value | previous_value
---+-------+---------------
1 | 10 | 10
2 | 20 | 10
3 | 30 | 20
4 | 40 | 30
5 | 50 | 40
Now apply where.
select id, value
from (
select id, value, lag(value, 1, value) over (order by value) previous_value
from your_table)
where previous_value < 25
Works for me.
id | value
----+------
1 | 10
2 | 20
3 | 30
Of course you have to have some policy on ties. For example, what happens if two rows have the same value and they are both first — do you want to keep both or only one of them. Or maybe you have some other criterion for breaking the tie (say, sort by id). But the idea is fairly simple.

you can try a query like this :
SELECT * FROM YourTableName WHERE Value < 25 OR ID IN (SELECT TOP 1 ID FROM YourTableName WHERE Value >= 25 ORDER BY Value)

in Oracle, you can try this (but see "That Young Man" answer, I think it's better than mine):
SELECT * FROM (
SELECT ID, NAME, VALUE, 1 AS RN
FROM YT
WHERE VALUE < 25
UNION ALL
SELECT ID, NAME, VALUE, ROW_NUMBER()OVER (ORDER BY VALUE) AS RN
FROM YT
WHERE VALUE > 25
) A
WHERE RN=1;

Related

SUM a column in SQL, based on DISTINCT values in another column, GROUP BY a third column

I'd appreciate some help on the following SQL problem:
I have a table of 3 columns:
ID Group Value
1 1 5
1 1 5
1 2 10
1 2 10
1 3 20
2 1 5
2 1 5
2 1 5
2 2 10
2 2 10
3 1 5
3 2 10
3 2 10
3 2 10
3 4 50
I need to group by ID, and I would like to SUM the values based on DISTINCT values in Group. So the value for a group is only accounted for once even though it may appear multiple for times for a particular ID.
So for IDs 1, 2 and 3, it should return 35, 15 and 65, respectively.
ID SUM
1 35
2 15
3 65
Note that each Group doesn't necessarily have a unique value
Thanks
the CTE will remove all duplicates, so if there a sdiffrenet values for ID and Group, it will be counted.
The next SELECT wil "GROUP By" ID
For Pstgres you would get
WITH CTE as
(SELECT DISTINCT "ID", "Group", "Value" FROM tablA
)
SELECT "ID", SUM("Value") FROM CTE GROUP BY "ID"
ORDER BY "ID"
ID | sum
-: | --:
1 | 35
2 | 15
3 | 65
db<>fiddle here
Given what we know at the moment this is what I'm thinking...
The CTE/Inline view eliminate duplicates before the sum occurs.
WITH CTE AS (SELECT DISTINCT ID, Group, Value FROM TableName)
SELECT ID, Sum(Value)
FROM CTE
GROUP BY ID
or
SELECT ID, Sum(Value)
FROM (SELECT DISTINCT * FROM TableName) CTE
GROUP BY ID

How to update a column for all rows by increment of 10

I have a situation where there are column (int) named 'position' in table. I need to update that column by increment of 10 for every row.
I can create a function for that, but can it be achieved by simple query?
Quick example of current values and desired ones:
| ID | POSITION | .... | WHAT I WANT POSITION TO BE |
1 10 xxx 10
2 21 xxx 20
3 22 xxx 30
5 30 xxx 40
.... etc
You can use row_number() and arithmetic:
select t.*,
10 * row_number() over (order by position) as new_position
from t;
If you want to update the value, then you can include this in an update:
update t
set position = tt.new_position
from (select t.*,
10 * row_number() over (order by position) as new_position
from t
) tt
where tt.id = t.id;

Some Case statement issue

I have two tables that has data like
table1
Id id_nm
1 per
2 per
3 org
table2
Id Lst_id l_nm up_dt
1 22 abc 9/10/2015
1 21 abs 10/12/2016
2 21 xzc 10/12/2013
2 23 xyz 10/21/2013
2 23 xnh 01/12/2013
Need to pick the l_nm where lst_id is 22. If that is not present then we need to pick the l_nm with the most recent updated date.
Id lst_id lnm up_dt
1 22 abc 9/10/2015
2 23 xyz 10/21/2013
can any one please help me in implementing it.
Simple way is to use row_number with a window clause to generate a custom sort order:
select id, lst_id, l_nm as lnm, up_dt
from (
select id
,lst_id
,l_nm
,up_dt
,row_number()
over (partition by id
order by case when lst_id = 22 then 1 else 2 end
,up_dt desc) as rn
from table2
) where rn = 1;

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.

Update duplicate latitude values by iteratively increasing margin

I have lat and long columns in an Oracle database table stored as regular numbers.
Some of which are duplicates. I'd like a way to add a very small margin to either column to eliminate duplication. Problem is, for each identical pair the number of duplicate records might vary. In this case I have to adjust the margin I add iteratively for each pair.
example:
ID | LAT | LONG
==================
1 | 1 | 1
2 | 1 | 1
3 | 1 | 1
in this case, I'd like to add a margin of .0003 to either column to eliminate the duplication, but I can't just blindly add that .0003 to IDs 2 and 3 because they would still be duplicates, so I have to do original_value + (margin*i) for i in (0...number of duplicate rows)
so I'd like to end up with something like this:
ID | LAT | LONG
1 | 1 | 1
2 | 1.0003 | 1
3 | 1.0006 | 1
How do I do this in SQL? I can mimic imperative programming apparently with cursors but it does not seem to be the SQL way. Can I somehow achieve this with INSERT INTO SELECT?
I don't know what your exact data looks like, but suppose you have this table, called tbl:
ID LAT LON
---------- ---------- ----------
1 20 25
2 30 33
3 30 33
4 55 60
5 55 60
6 55 60
You could run the following:
select id,
case when rn > 1 then lat+rn-1 else lat end as lat,
lon
from(
select t.*,
row_number() over(partition by lat, lon order by id) as rn
from tbl t
) x;
To get:
ID LAT LON
---------- ---------- ----------
1 20 25
2 30 33
3 31 33
4 55 60
5 56 60
6 57 60
Notice how IDs 2 and 3 were dups, and IDs 4, 5, and 6, were dups. They are no longer exact dups because the lat value has increased, sequentially, to make the rows not duplicates. They go up by one for each next duplicate.
Fiddle: http://sqlfiddle.com/#!4/ef959/1/0
Edit (based on your edit)
select id,
case when rn > .0003 then lat+rn-.0003 else lat end as lat,
lon
from(
select t.*,
row_number() over(partition by lat, lon order by id)*.0003 as rn
from tbl t
) x;
The above will ascend by .0003 rather than 1.
See new fiddle here: http://sqlfiddle.com/#!4/21506/6/0