Counting number of sorted subgroups inpostgresql - sql

I would like to count the number of sorted subgroups in the table below :
id nmb
1 11
2 12
3 13
4 22
5 23
6 31
7 32
8 33
9 11
10 12
11 13
12 12
13 13
14 21
15 22
16 11
17 12
18 13
19 14
And want to obtain something like this in postgresql 8.4 :
id nmb local
1 11 1
2 12 1
3 13 1
4 22 1
5 23 1
6 31 1
7 32 1
8 33 1
9 11 2
10 12 2
11 13 2
12 12 3
13 13 3
14 21 3
15 22 3
16 11 4
17 12 4
18 13 4
19 14 4
EDIT: the last few numbers on the 'local' column were wrong. Corrected!
Thank you.

I though i finally understood what you want, consecutively growing values:
select id, nmb,
sum(flag)
over (order by id
rows unbounded preceding) as local
from
(
select
id, nmb,
case
when lag(nmb)
over (order by id) < nmb
then 0
else 1
end as flag
from t
) as dt
order by id
But group 4 doesn't fit
Edit: Now they fit :-)

It appears that you are trying to enumerate the groups, where a group starts with a lower nmb value than the previous row ("previous" defined by the id order).
The idea is to identify the start of a group by using lag(). Then take the cumulative sum to get the group identifier that you want:
select id, nmb, sum(StartFlag) over (order by id) as local
from (select id, nmb,
lag(nmb) over (order by id) as lastnmb,
(case when lag(nmb) over (order by id) < nmb then 0
else 1
end) as StartFlag
from t
) t
order by id;

SQL Fiddle
select
id, nmb,
row_number() over(partition by nmb order by id)
from t
order by id

Related

Select commonly chosen desires collage by students after first 5 rows each group

With subquery I need to select after first five rows for each group of id_student and must common values of id_desireCollage between id_student.
More explain : select common collages for each student desires after his five chosen desires
ID
id_desireCollage
id_student
1
1
1
2
2
1
3
3
1
4
4
1
5
5
1
6
8
1
7
9
1
8
7
1
9
2
2
10
12
2
11
1
2
12
3
2
13
6
2
14
5
2
15
8
2
16
9
2
17
7
2
18
4
3
19
3
3
20
2
3
21
1
3
22
8
3
23
9
3
24
7
3
25
5
3
Something like
select id_desireCollage
from
(select *
from desires ds
where ds.id_desireCollage = desires.id_desireCollage)
group by (id_student)
having count(*) > 5
Expected result is:
id_desireCollage
7
9
Try the following:
select id_desireCollage
from
(
select d.*,
row_number() over (partition by id_student order by ID) as rn
from desires d
) T
where rn > 5
group by id_desireCollage
order by count(*) desc
fetch first 1 row with ties
If you don't want to use the row number function (as you commented), you may try the following - supposing there are no gaps in the ID column:
select id_desireCollage
from desires d
where id >=
(
select min(id)+5
from desires t
where t.id_student = d.id_student
)
group by id_desireCollage
order by count(*) desc
fetch first 1 row with ties
See demo
As suggested by #MatBailie, if you meant by common, that all students have selected the id_desireCollage value then you could use the following:
select id_desireCollage
from desires d
where id >=
(
select min(id)+5
from desires t
where t.id_student = d.id_student
)
group by id_desireCollage
having count(*)=
(
select count(distinct id_student)
from desires
)

SQL Ignoring duplicate values if ID difference larger than

Lets say I have a simple table:
ID value
1 15
2 30
3 **10**
4 **10**
5 16
6 20
7 **15**
8 **15**
9 40
10 70
11 **50**
12 **50**
13 19
14 11
15 3
My select should ignore consecutive double values. I know how to do that - I am using lead function
But this eliminates all consecutive duplicates.
SELECT [DetectorParameterValue]
FROM (
SELECT lead(DetectorParameterValue,1) over (partition by runid order by runtime) AS prev_DetectorParameterValue
FROM table_Detector
WHERE RunID = #run_id
) AS [InnerDetector]
WHERE (prev_DetectorParameterValue is null or or prev_DetectorParameterValue <> DetectorParameterValue
But it should ignore them only if ID diff is more than 5.
So my select should be
ID Value
1 15
2 30
3 10
5 16
6 20
7 15
8 15
9 40
10 70
11 50
13 19
14 11
15 3
ID 4 and 12 should be ignored but ID 8 should not because ID 8 - ID 4 is not > 5.
Is there a way to do this?
Thanks in advance.
This is a gaps and islands problem in disguise. One approach uses the difference in row numbers method.
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY ID) rn1,
ROW_NUMBER() OVER (PARTITION BY value ORDER BY ID) rn2
FROM yourTable
),
cte2 AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY value, rn1 - rn2 ORDER BY ID) rn
FROM cte
)
SELECT ID, value
FROM cte2
WHERE rn = 1
ORDER BY ID;

row_number() but only increment value after a specific value in a column

Query: SELECT (row_number() OVER ()) as grp, * from tbl
Edit: the rows below are returned by a pgrouting shortest path function and it does have a sequence.
seq grp id
1 1 8
2 2 3
3 3 2
4 4 null
5 5 324
6 6 82
7 7 89
8 8 null
9 9 1
10 10 2
11 11 90
12 12 null
How do I make it so that the grp column is only incremented after a null value on id - and also keep the same order of rows
seq grp id
1 1 8
2 1 3
3 1 2
4 1 null
5 2 324
6 2 82
7 2 89
8 2 null
9 3 1
10 3 2
11 3 90
12 3 null
demo:db<>fiddle
Using a cumulative SUM aggregation is a possible approach:
SELECT
SUM( -- 2
CASE WHEN id IS NULL THEN 1 ELSE 0 END -- 1
) OVER (ORDER BY seq) as grp,
id
FROM mytable
If the current (ordered!) value is NULL, then make it 1, else 0. Now you got a bunch of zeros, delimited by a 1 at each NULL record. If you'd summerize these values cumulatively, at each NULL record, the sum increased.
Execution of the cumulative SUM() using window functions
This yields:
0 8
0 3
0 2
1 null
1 324
1 82
1 89
2 null
2 1
2 2
2 90
3 null
As you can see, the groups start with the NULL records, but you are expecting to end it.
This can be achieved by adding another window function: LAG(), which moves the records to the next row:
SELECT
SUM(
CASE WHEN next_id IS NULL THEN 1 ELSE 0 END
) OVER (ORDER BY seq) as grp,
id
FROM (
SELECT
LAG(id) OVER (ORDER BY seq) as next_id,
seq,
id
FROM mytable
) s
The result is your expected one:
1 8
1 3
1 2
1 null
2 324
2 82
2 89
2 null
3 1
3 2
3 90
3 null

Escaping x number of rows in Select query

I want rows from the entire table but I only want to select rows 1,5,10,15,20,25
Table
ID Col1 Col2……….
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
I want to select in my result set
Id Col1 Col2……….
1
5
10
15
20
Any suggestions would be appreciated.
Thank you
You can use:
where id = 1 or
id % 5 = 0
The % is the modulo operator. Some databases use a mod() function instead.
If id has gaps or other issues, you may want to do this with row_number():
select t.*
from (select t.*, row_number() over (order by id) as seqnum
from t
) t
where seqnum = 1 or seqnm % 5 = 0;

SQL Select to limit results to X of each id value in a specific column

I am trying to figure out how to select up to lets say 2 results per key that I have in my table.
My table looks like the following
uid map
1 11
1 12
1 16
1 21
1 26
2 1
2 11
2 14
2 12
2 22
3 12
3 15
3 16
What Im looking to do is select up to 2 rows of data for each uid
example results:
uid map
1 11
1 12
2 1
2 11
3 12
3 15
Thanks for any help you can provide!
Using ANSI-standard SQL, the simplest way is row_number():
select uid, map
from (select t.*, row_number() over (partition by uid order by map) as seqnum
from t
) t
where seqnum <= 2;
SELECT *
FROM table1 a
WHERE
(
SELECT COUNT(*)
FROM table1 b
WHERE a.uid = b.uid AND
a.map <= b.map
) <= 2