Select TOP 2 values for each group - sql

I'm having problem with getting only TOP 2 values for each group (groups are in column).
Example :
ID Group Value
1 A 30
2 A 150
3 A 40
4 A 70
5 B 0
6 B 100
7 B 90
I expect my output to be
ID Group Value
1 A 150
2 A 70
3 B 100
4 B 90
Simply, for each group I want just 2 rows with the highest Value

Most databases support the ANSI standard row_number() function. You would use it as:
select group, value
from (select t.*,
row_number() over (partition by group order by value desc) as seqnum
from t
) t
where seqnum <= 2;
To set the id you can use row_number() in the outer query:
select row_number() over (order by group, value) as id,
group, value
from (select t.*,
row_number() over (partition by group order by value desc) as seqnum
from t
) t
where seqnum <= 2;
However, changing the id seems suspicious.

You can use CTE with rank function ROW_NUMBER() .
Here is query to get your result.
;WITH cte AS
( SELECT Group, value,
ROW_NUMBER() OVER (PARTITION BY Group ORDER BY value DESC) AS rn
FROM test
)
SELECT Group, value FROM cte
WHERE rn <= 2
ORDER BY value

Related

How to Rank By Partition with island and gap issue

Is it possible to rank item by partition without use CTE method
Expected Table
item
value
ID
A
10
1
A
20
1
B
30
2
B
40
2
C
50
3
C
60
3
A
70
4
A
80
4
By giving id to the partition to allow agitated function to work the way I want.
item
MIN
MAX
ID
A
10
20
1
B
30
40
2
C
50
60
3
A
70
80
4
SQL Version: Microsoft SQL Sever 2017
Assuming that the value column provides the intended ordering of the records which we see in your question above, we can try using the difference in row numbers method here. Your problem is a type of gaps and islands problem.
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY value) rn1,
ROW_NUMBER() OVER (PARTITION BY item ORDER BY value) rn2
FROM yourTable
)
SELECT item, MIN(value) AS [MIN], MAX(value) AS [MAX], MIN(ID) AS ID
FROM cte
GROUP BY item, rn1 - rn2
ORDER BY MIN(value);
Demo
If you don't want to use a CTE here, for whatever reason, you may simply inline the SQL code in the CTE into the bottom query, as a subquery:
SELECT item, MIN(value) AS [MIN], MAX(value) AS [MAX], MIN(ID) AS ID
FROM
(
SELECT *, ROW_NUMBER() OVER (ORDER BY value) rn1,
ROW_NUMBER() OVER (PARTITION BY item ORDER BY value) rn2
FROM yourTable
) t
GROUP BY item, rn1 - rn2
ORDER BY MIN(value);
You can generate group IDs by analyzing the previous row item value that could be obtained with the LAG function and finally use GROUP BY to get the minimum and maximum value in item groups.
SELECT
item,
MIN(value) AS "min",
MAX(value) AS "max",
group_id + 1 AS id
FROM (
SELECT
*,
SUM(CASE WHEN item = prev_item THEN 0 ELSE 1 END) OVER (ORDER BY value) AS group_id
FROM (
SELECT
*,
LAG(item, 1, item) OVER (ORDER BY value) AS prev_item
FROM t
) items
) groups
GROUP BY item, group_id
Query produces output
item
min
max
id
A
10
20
1
B
30
40
2
C
50
60
3
A
70
80
4
You can check a working demo here

increment if not same value of next column in SQL

I am trying to use the Row Number in SQL. However, it's not giving desired output.
Data :
ID Name Output should be
111 A 1
111 B 2
111 C 3
111 C 3
111 A 4
222 A 1
222 A 1
222 B 2
222 C 3
222 B 4
222 B 4
This is a gaps-and-islands problem. As a starter: for the question to just make sense, you need a column that defines the ordering of the rows - I assumed ordering_id. Then, I would recommend lag() to get the "previous" name, and a cumulative sum() that increases everytime the name changes in adjacent rows:
select id, name,
sum(case when name = lag_name then 0 else 1 end) over(partition by id order by ordering_id) as rn
from (
select t.*, lag(name) over(partition by id order by ordering_id) lag_name
from mytable t
) t
SQL Server 2008 makes this much trickier. You can identify the adjacent rows using a difference of rows numbers. Then you can assign the minimum id in each island and use dense_rank():
select t.*,
dense_rank() over (partition by name order by min_ordcol) as output
from (select t.*,
min(<ordcol>) over (partition by name, seqnum - seqnum_2) as min_ordcol
from (select t.*,
row_number() over (partition by name order by <ordcol>) as seqnum,
row_number() over (partition by name, id order by <ordcol>) as seqnum_2
from t
) t
) t;

Find max uninterrupted interval

How I can find max uninterrupted interval in column?
Example
ID Result
1 1
2 2
3 3
4 4
5 5
6 6
10
11
12
You can use row_number(). Here is a simple way to get the first and lsat values:
select top (1) with ties min(id), max(id)
from (select t.*, row_number() over (order by id) as seqnum
from t
) t
group by (id - seqnum)
order by count(*) desc;
To get the actual original rows requires another level of window functions:
select top (1) with ties
from (select t.*, count(*) over (partition by id - seqnum) as cnt
from (select t.*, row_number() over (order by id) as seqnum
from t
) t
) t
order by cnt desc, id;

Using ROW_NUMBER in CTE

I am trying for a CTE with a ROW_NUMBER function.
Query:
with Demo as
(
select *, ROW_NUMBER() over (partition by GID, MID order by MMID) as ROWNUMBER from Table1 where DateEntered > '2015-06-13 00:00:00.000'
)
select * from Demo
Here, the result I get is
GID MID ROWNUMBER
1 1 1
1 2 1
1 2 2
1 2 3
2 1 1
2 2 1
2 2 2
2 3 5
2 4 4
Now, I want to get all the rows where combination of GID,MID has max row number value. But a condition here is that for those rows, the combination of GID,MID should also have 1.
In simple terms, get me the rows with max row number value, if that combination of gid,mid has rownumber=1.
The output I desire is
GID MID ROWNUMBER
1 1 1
1 2 3
2 1 1
2 2 2
I hope i did not made it complex. Can anyone pls inform me on how to do this ?
with Demo as
(
select *, ROW_NUMBER() over (partition by GID, MID order by MMID) as RN
from Table1 where DateEntered > '2015-06-13 00:00:00.000'
)
, x as
(select gid, mid
from demo
where RN = 1
)
select demo.gid, demo.mid, max(demo.rn) as rownumb
from demo left join x
on x.gid = demo.gid and x.mid = demo.mid
group by demo.gid, demo.mid;
You can use max to select the highest rownumber per mid, gid combination.
If you don't need the row number value, just use desc instead of asc and a filter:
with Demo as (
select t.*,
ROW_NUMBER() over (partition by GID, MID order by MID DESC) as seqnum
from Table1
where DateEntered > '2015-06-13'
)
select *
from Demo
where seqnum = 1;
If you do want a more meaningful ROWNUMBER in the output, then use two calculations in the CTE.
The max of row_number() is just count.
select GID, MID, COUNT(*) as ROWNUMBER
from Table1
where DateEntered > '2015-06-13 00:00:00.000'
group by GID, MID
This should work for you:
with Demo as
(
select *,
ROW_NUMBER() over (partition by GID, MID order by MMID) as ROWNUMBER
from Table1
where DateEntered > '2015-06-13 00:00:00.000'
)
select GID,
MID,
MAX(ROWNUMBER) as MaxROWNUMBER
from Demo
GROUP BY GID,MID;

Count consecutive duplicate values in SQL

I have a table like so
ID OrdID Value
1 1 0
2 2 0
3 1 1
4 2 1
5 1 1
6 2 0
7 1 0
8 2 0
9 2 1
10 1 0
11 2 0
I want to get the count of consecutive value where the value is 0. Using the example above the result will be 3 (Rows 6, 7 and 8). I am using sql server 2008 r2.
I am going to presume that id is unique and increasing. You can get counts of consecutive values by using the different of row numbers. The following counts all sequences:
select grp, value, min(id), max(id), count(*) as cnt
from (select t.*,
(row_number() over (order by id) - row_number() over (partition by value order by id)
) as grp
from table t
) t
group by grp, value;
If you want the longest sequence of 0s:
select top 1 grp, value, min(id), max(id), count(*) as cnt
from (select t.*,
(row_number() over (order by id) - row_number() over (partition by value order by id)
) as grp
from table t
) t
group by grp, value
having value = 0
order by count(*) desc
A query using not exists to find consecutive 0s
select top 1 min(t2.id), max(t2.id), count(*)
from mytable t
join mytable t2 on t2.id <= t.id
where not exists (
select 1 from mytable t3
where t3.id between t2.id and t.id
and t3.value <> 0
)
group by t.id
order by count(*) desc
http://sqlfiddle.com/#!3/52989/3