rank a column with out ordering in oracle - sql

I have the data as below, When I apply dense_rank by ordering id column, I am getting rank according to the order of integers but I need to rank as the records are displayed when run a query:
Data from query:
Rid id
8100 161
8101 2
8102 2
8103 2
8104 156
When I apply dense_rank over order by id then I am getting
Rid id rank
8100 161 3
8101 2 1
8102 2 1
8103 2 1
8104 156 2
But my requirement is to get in below way:
Rid id rank
8100 161 1
8101 2 2
8102 2 2
8103 2 2
8104 156 3
Used row_number as well but the result is not as expected, not sure what option would be the better way.
Any help is appreciated.
Thanks
Edit------------------------------
Query used
Select rid, id,dense_rank() over (order by id) row_num
from table

I have adjusted solution from here: DENSE_RANK according to particular order for your need.
I am not sure if I should mark this as duplicate because on this link above there is no ORACLE tag. If more experience members think I should please do comment and I will do so and delete this answer.
Here is the adjusted code and demo:
SELECT t2.rid
, t2.id
, DENSE_RANK() OVER (ORDER BY t2.max_rid)
FROM (
SELECT MAX(t1.rid) OVER (PARTITION BY t1.grupa) AS max_rid
, t1.rid
, t1.id
FROM (
SELECT rid
, id
,ROW_NUMBER() OVER (ORDER BY rid) - ROW_NUMBER() OVER (PARTITION BY id ORDER BY rid) AS grupa
FROM test_table) t1 ) t2
ORDER BY rid
DEMO

You can use sum() aggregation containing (order by rid) after getting the values from lag() analytic function within the first query
with tab( rid,id ) as
(
select 8100,161 from dual union all
select 8101,2 from dual union all
select 8102,2 from dual union all
select 8103,2 from dual union all
select 8104,156 from dual
), t2 as
(
select t.*, lag(id,1,0) over (order by rid) lg
from tab t
)
select rid, id, sum(case when lg!=id then 1 else 0 end) over (order by rid) as row_num
from t2
Demo

Related

How to compare multiple rows

I have a table with data like following, want to return those group_id with unique data. Both group_id 3 and 4 have two component 123 and 456, so they are "duplicated", we just need to return the smaller group_id, that's 3. Also group_id 5 doesn't have a duplication, it can be returned. So we want group_id 3 and 5 to be returned.
How can I write a SQL query against postgres database to achieve that? Thank you!
id
group_id
component_id
1
3
123
2
3
456
3
4
123
4
4
456
5
5
123
Use 2 levels of aggregation:
SELECT MIN(group_id) group_id
FROM (
SELECT group_id, STRING_AGG(component_id::text, ',' ORDER BY component_id) components
FROM tablename
GROUP BY group_id
) t
GROUP BY components;
See the demo.
SELECT group_id, MIN(component_id)
FROM MyTable
GROUP BY group_id
HAVING COUNT(*) > 1
Here's a method to assign the group_id's to the component_id's.
It uses a recursive CTE with arrays to find the possible combinations.
The recursion starts from the lonely group_id's.
Then the next CTE picks one of the longest combinations.
WITH RECURSIVE RCTE AS (
SELECT id, group_id, component_id
, 1 as Lvl
, array[group_id] as group_ids
, array[component_id] as component_ids
FROM YourTable
WHERE group_id IN (
SELECT group_id
FROM YourTable
GROUP BY group_id
HAVING COUNT(*) = 1
)
UNION ALL
SELECT t.id, t.group_id, t.component_id
, Lvl+1
, cte.group_ids || t.group_id
, cte.component_ids || t.component_id
FROM RCTE cte
JOIN YourTable t
ON t.group_id != ALL(group_ids)
AND t.component_id != ALL(component_ids)
)
, CTE_ARRAYS AS (
SELECT group_ids, component_ids
FROM RCTE
ORDER BY array_length(group_ids, 1) desc, Lvl desc
LIMIT 1
)
SELECT a.group_id, a.component_id
FROM CTE_ARRAYS c
CROSS JOIN LATERAL UNNEST(c.group_ids, c.component_ids) WITH ORDINALITY AS a(group_id, component_id)
ORDER BY a.group_id;
group_id
component_id
3
456
5
123
db<>fiddle here

SQL Server : create group of N rows each and give group number for each group

I want to create a SQL query that SELECT a ID column and adds an extra column to the query which is a group number as shown in the output below.
Each group consists of 3 rows and should have the MIN(ID) as a GroupID for each group. The order by should be ASC on the ID column.
ID GroupNr
------------
100 100
101 100
102 100
103 103
104 103
105 103
106 106
107 106
108 106
I've tried solutions with ROW_NUMBER() and DENSE_RANK(). And also this query:
SELECT
*, MIN(ID) OVER (ORDER BY ID ASC ROWS 2 PRECEDING) AS Groupnr
FROM
Table
ORDER BY
ID ASC
Use row_number() to enumerate the rows, arithmetic to assign the group and then take the minimum of the id:
SELECT t.*, MIN(ID) OVER (PARTITION BY grp) as groupnumber
FROM (SELECT t.*,
( (ROW_NUMBER() OVER (ORDER BY ID) - 1) / 3) as grp
FROM Table
) t
ORDER BY ID ASC;
It is possible to do this without a subquery, but the logic is rather messy:
select t.*,
(case when row_number() over (order by id) % 3 = 0
then lag(id, 2) over (order by id)
when row_number() over (order by id) % 3 = 2
then lag(id, 1) over (order by id)
else id
end) as groupnumber
from table t
order by id;
Assuming you want the lowest value in the group, and they are always groups of 3, rather than the NTILE (as Saravantn suggests, which splits the data into that many even(ish) groups), you could use a couple of window functions:
WITH Grps AS(
SELECT V.ID,
(ROW_NUMBER() OVER (ORDER BY V.ID) -1) / 3 AS Grp
FROM (VALUES(100),
(101),
(102),
(103),
(104),
(105),
(106),
(107),
(108))V(ID))
SELECT G.ID,
MIN(G.ID) OVER (PARTITION BY G.Grp) AS GroupNr
FROM Grps G;
SELECT T2.ID, T1.ID
FROM (
SELECT MIN(ID) AS ID, GroupNr
FROM
(
SELECT ID, ( Row_number()OVER(ORDER BY ID) - 1 ) / 3 + 1 AS GroupNr
FROM Table
) AS T1
GROUP BY GroupNr
) AS T1
INNER JOIN (
SELECT ID, ( Row_number()OVER(ORDER BY ID) - 1 ) / 3 + 1 AS GroupNr
FROM Table
) T2 ON T1.GroupNr = T2.GroupNr

sql - for every unique value in column sample 2 different values from another column

I am stuck on a sql aggregate problem.
Consider the following table/view:
Column1 Column2
1 2564
2 6550
1 3578
2 6548
2 4789
1 9876
I would like to design a query to do the following:
For every distinct Column1 value, sample 2x records. The sampling strategy could be some some sort of bootstrapping/resampling as there might not too many data points.
Thus the table will become:
Column1 Column2
1 9876
1 3578
2 6548
2 6550
Platform: MS SQL
Any answers are appreciated.
For a random sample without replacement:
select t.*
from (select t.*,
row_number() over (partition by column1 order by newid()) as seqnum
from t
) t
where seqnum <= 2;
Or, alternatively:
select top (2) with ties t.*
from t
order by row_number() over (partition by id order by newid());
For a random sample with replacement:
With replacement:
select *
from ((select top (1) with ties t.*
from t
order by row_number() over (partition by id order by newid())
)
union all
(select top (1) with ties t.*
from t
order by row_number() over (partition by id order by newid())
)
) x;

Oracle select query to filter rows

Say I have a table with the following values
id (PK) a_num a_code effect_dt expire_dt
32 1234 abcd 01/01/2015 05/30/2015
9 1234 abcd 06/01/2015 12/31/2015
5 1234 efgh 01/01/2015 05/30/2015
14 1234 efgh 06/01/2015 12/31/2015
How can I select just one record from a_num,a_code pair. Either Id's 1,3 or 2,4? There may be scenarios where there are more than 2 records for a a_num,a_code pair.
UPDATE - ID will not necessarily always be in order, it is just a primary key.
This will give you rows 1 and 3
Select * from (
Select * , Row_number() Over(Partition by a_num, a_code order by id) r_num from Your_Table ) result
Where r_num = 1
Just use DESC in order by and you will get rows 2 and 4
Select * from (
Select * , Row_number() Over(Partition by a_num, a_code order by id desc) r_num from Your_Table ) result
Where r_num = 1
One way would be to use the row_number window function:
SELECT id, a_num, a_code, effect_dt, expire_dt
FROM (SELECT id, a_num, a_code, effect_dt, expire_dt,
ROW_NUMBER() OVER (PARTITION BY a_num, a_code
ORDER BY 1) AS rn
FROM mytable) t
WHERE rn = 1

SELECT records until new value SQL

I have a table
Val | Number
08 | 1
09 | 1
10 | 1
11 | 3
12 | 0
13 | 1
14 | 1
15 | 1
I need to return the last values where Number = 1 (however many that may be) until Number changes, but do not need the first instances where Number = 1. Essentially I need to select back until Number changes to 0 (15, 14, 13)
Is there a proper way to do this in MSSQL?
Based on following:
I need to return the last values where Number = 1
Essentially I need to select back until Number changes to 0 (15, 14,
13)
Try (Fiddle demo ):
select val, number
from T
where val > (select max(val)
from T
where number<>1)
EDIT: to address all possible combinations (Fiddle demo 2)
;with cte1 as
(
select 1 id, max(val) maxOne
from T
where number=1
),
cte2 as
(
select 1 id, isnull(max(val),0) maxOther
from T
where val < (select maxOne from cte1) and number<>1
)
select val, number
from T cross join
(select maxOne, maxOther
from cte1 join cte2 on cte1.id = cte2.id
) X
where val>maxOther and val<=maxOne
I think you can use window functions, something like this:
with cte as (
-- generate two row_number to enumerate distinct groups
select
Val, Number,
row_number() over(partition by Number order by Val) as rn1,
row_number() over(order by Val) as rn2
from Table1
), cte2 as (
-- get groups with Number = 1 and last group
select
Val, Number,
rn2 - rn1 as rn1, max(rn2 - rn1) over() as rn2
from cte
where Number = 1
)
select Val, Number
from cte2
where rn1 = rn2
sql fiddle demo
DEMO: http://sqlfiddle.com/#!3/e7d54/23
DDL
create table T(val int identity(8,1), number int)
insert into T values
(1),(1),(1),(3),(0),(1),(1),(1),(0),(2)
DML
; WITH last_1 AS (
SELECT Max(val) As val
FROM t
WHERE number = 1
)
, last_non_1 AS (
SELECT Coalesce(Max(val), -937) As val
FROM t
WHERE EXISTS (
SELECT val
FROM last_1
WHERE last_1.val > t.val
)
AND number <> 1
)
SELECT t.val
, t.number
FROM t
CROSS
JOIN last_1
CROSS
JOIN last_non_1
WHERE t.val <= last_1.val
AND t.val > last_non_1.val
I know it's a little verbose but I've deliberately kept it that way to illustrate the methodolgy.
Find the highest val where number=1.
For all values where the val is less than the number found in step 1, find the largest val where the number<>1
Finally, find the rows that fall within the values we uncovered in steps 1 & 2.
select val, count (number) from
yourtable
group by val
having count(number) > 1
The having clause is the key here, giving you all the vals that have more than one value of 1.
This is a common approach for getting rows until some value changes. For your specific case use desc in proper spots.
Create sample table
select * into #tmp from
(select 1 as id, 'Alpha' as value union all
select 2 as id, 'Alpha' as value union all
select 3 as id, 'Alpha' as value union all
select 4 as id, 'Beta' as value union all
select 5 as id, 'Alpha' as value union all
select 6 as id, 'Gamma' as value union all
select 7 as id, 'Alpha' as value) t
Pull top rows until value changes:
with cte as (select * from #tmp t)
select * from
(select cte.*, ROW_NUMBER() over (order by id) rn from cte) OriginTable
inner join
(
select cte.*, ROW_NUMBER() over (order by id) rn from cte
where cte.value = (select top 1 cte.value from cte order by cte.id)
) OnlyFirstValueRecords
on OriginTable.rn = OnlyFirstValueRecords.rn and OriginTable.id = OnlyFirstValueRecords.id
On the left side we put an original table. On the right side we put only rows whose value is equal to the value in first line.
Records in both tables will be same until target value changes. After line #3 row numbers will get different IDs associated because of the offset and will never be joined with original table:
LEFT RIGHT
ID Value RN ID Value RN
1 Alpha 1 | 1 Alpha 1
2 Alpha 2 | 2 Alpha 2
3 Alpha 3 | 3 Alpha 3
----------------------- result set ends here
4 Beta 4 | 5 Alpha 4
5 Alpha 5 | 7 Alpha 5
6 Gamma 6 |
7 Alpha 7 |
The ID must be unique. Ordering by this ID must be same in both ROW_NUMBER() functions.