Contiguous Group By - sql

I have the following table:
SELECT *
FROM mytable
ORDER BY id;
id name code time
1 A 111 1
2 A 111 2
3 A 888 3
4 A 888 4
5 A 888 5
6 A 888 6
7 A 888 7
8 A 111 8
9 A 111 9
10 A 111 10
I need to get a result like this:
name code times_between
A 111 1,2
A 888 3,7
A 111 8,10
Is it possible to group by "chunks"?
I need to make a distinction based on time, so I can't just group by name,code and get the first and last element only.

One way is this:
with the_table(id, name , code , time) as(
select 1, 'A',111 , 1 union all
select 2, 'A',111 , 2 union all
select 3, 'A',888 , 3 union all
select 4, 'A',888 , 4 union all
select 5, 'A',888 , 5 union all
select 6, 'A',888 , 6 union all
select 7, 'A',888 , 7 union all
select 8, 'A',111 , 8 union all
select 9, 'A',111 , 9 union all
select 10, 'A',111 , 10
)
select name, code, min(time) ||','|| max(time) from (
select name, code, time, id,
row_number() over(order by id) -
row_number() over(partition by name , code order by id) as grp
from the_table
) t
group by name, code, grp
order by min(id)
(I forgot and just can't find/remember the name of technique, which creates groups grp)

Related

GROUP BY ID and select MAX

Good Evening,
I am working on a table like this in Oracle:
ID
BALANCE
SEQ
1
102
13
1
119
15
2
50
4
3
20
11
3
15
10
3
45
9
4
90
5
5
67
20
5
12
19
6
20
1
I want to select, for each ID, the BALANCE having MAX(SEQ).
So final result would be:
ID
BALANCE
SEQ
1
119
15
2
50
4
3
20
11
4
90
5
5
67
20
6
20
1
How can I do that?
I've tried several Group by queries but with no success.
Thanks for any help
One method is aggregation using keep:
select id,
max(balance) keep (dense_rank first order by seq desc) as balance,
max(seq)
from t
group by id;
You may use normal rank()
SELECT ID, BALANCE, SEQ FROM (
select
ID, BALANCE, SEQ, RANK() OVER (PARTITION BY ID ORDER BY SEQ DESC) ranks
from t
) WHERE ranks = 1
sample demo
SELECT ID, BALANCE, SEQ FROM (
SELECT ID, BALANCE, SEQ, RANK() OVER (PARTITION BY ID ORDER BY SEQ DESC) ranks
FROM (
SELECT 1 ID, 102 BALANCE, 13 SEQ FROM dual UNION all
SELECT 1, 119, 15 FROM dual UNION all
SELECT 2, 50, 4 FROM dual UNION all
SELECT 3, 20, 11 FROM dual UNION all
SELECT 3, 15, 10 FROM dual UNION all
SELECT 3, 45, 9 FROM dual UNION all
SELECT 4, 90, 5 FROM dual UNION all
SELECT 5, 67, 20 FROM dual UNION all
SELECT 5, 12, 19 FROM dual UNION all
SELECT 6, 20, 1 FROM dual
)
) WHERE ranks = 1
you can add it in your Big query as below
SELECT ID, BALANCE, SEQ FROM (
select
ID, BALANCE, SEQ, RANK() OVER (PARTITION BY ID ORDER BY SEQ DESC) ranks
from (**YOUR BIG QUERY HERE**)
) WHERE ranks = 1

Oracle - generate a running number by group

I need to generate a running number / group sequence inside a select statement for a group of data.
For example
Group Name Sequence
1 a 1
1 b 2
1 c 3
2 d 1
2 e 2
2 f 3
So for each group the sequence should be a running number starting with 1 depending on the order of column"Name".
I already pleayed around with Row_Number() and Level but I couldn't get a solution.
Any idea how to do it?
Analytic functions help.
SQL> with test (cgroup, name) as
2 (select 1, 'a' from dual union all
3 select 1, 'b' from dual union all
4 select 1, 'c' from dual union all
5 select 2, 'd' from dual union all
6 select 2, 'e' from dual union all
7 select 2, 'f' from dual
8 )
9 select cgroup,
10 name,
11 row_number() over (partition by cgroup order by name) sequence
12 from test
13 order by cgroup, name;
CGROUP N SEQUENCE
---------- - ----------
1 a 1
1 b 2
1 c 3
2 d 1
2 e 2
2 f 3
6 rows selected.
SQL>
Try this
SELECT
"Group",
Name,
DENSE_RANK() OVER (PARTITION BY "Group" ORDER BY Name) AS Sequence
FROM table;

Oracle Sql SUM MAX

I have following scenario:
ID Campus Credit_Hr
===== ====== ====
1 MIC 3
1 Warrens 4
1 Online 3
1 Online 3
2 MIC 5
2 Warrens 3
2 Online 6
3 Online 3
3 Online 3
3 West 2
4 Warrens 3
4 MIC 3
4 West 7
5 Online 3
5 West 3
5 East 3
Warrens and MIC are major campus. So, when Warrens and MIC has equal credit hr, like in ID 4, chose either Warrens / MIC
For ID 1: Warrens > MIC , chose Warrens though sum(Online) = 6 and is greater
For ID 2: MIC> Warrens, chose MIC
For ID 3: no Major Campus (Warrens/MIC) so chose max credit hr. er sum(online) is maximum so chose Online
For ID 5: West / East /Online all are minor campus, so chose any of them.
There are more than 50 campuses in real.
Assign information about MAJOR campuses, then use this column for ordering, in addition to the sum of hours:
dbfiddle demo
select *
from (
select a.*, row_number() over (partition by id order by major, sm desc) rn
from (
select id, campus,
case when campus in ('MIC', 'Warrens') then 1 else 2 end major,
sum(credit_hr) over (partition by id, campus) sm
from t) a)
where rn = 1
If all you need is to select max credit hours for each ID, but in such a way that if credit hours exist for 'MIC' or 'Warrens' for a given ID, then all other campuses for the same ID should be ignored, then the most efficient way is to use the FIRST aggregate function, like so:
with
sample_data(id, campus, credit_hr) as (
select 1, 'MIC' , 3 from dual union all
select 1, 'Warrens', 4 from dual union all
select 1, 'Online' , 3 from dual union all
select 1, 'Online' , 3 from dual union all
select 2, 'MIC' , 5 from dual union all
select 2, 'Warrens', 3 from dual union all
select 2, 'Online' , 6 from dual union all
select 3, 'Online' , 3 from dual union all
select 3, 'Online' , 3 from dual union all
select 3, 'West' , 2 from dual union all
select 4, 'Warrens', 3 from dual union all
select 4, 'MIC' , 3 from dual union all
select 4, 'West' , 7 from dual union all
select 5, 'Online' , 3 from dual union all
select 5, 'West' , 3 from dual union all
select 5, 'East' , 3 from dual
)
select id,
max(credit_hr) keep (dense_rank first
order by case when campus in ('MIC', 'Warrens') then 0 end)
as max_hr
from sample_data
group by id
order by id
;
ID MAX_HR
----- ------------------
1 4
2 5
3 3
4 3
5 3
You can also modify the query (add more columns) to show whether the max was from a main campus (that is, if that ID had ANY credit hours from one of the major campuses), and/or to show which campus had the max hours for that ID (or one of the campuses, if there was a tie for most hours).

SQLServer Query Windowing

I have a table like below
select 1 group_rank, 1 row_rank union all
select 1 , 2 union all
select 1 , 3 union all
select 1 , 4 union all
select 1 , 5 union all
select 2 , 1 union all
select 2 , 2 union all
select 2 , 3 union all
select 2 , 4 union all
select 2 , 5 union all
select 3 , 1 union all
select 3 , 2 union all
select 3 , 3 union all
select 3 , 4 union all
select 3 , 5 union all
select 4 , 1 union all
select 4 , 2 union all
select 4 , 3 union all
select 4 , 4 union all
select 4 , 5
I want to break row_rank further based upon size. If my size is 2, split the row_rank further like below. Output third column should be like below
select 1 group_rank, 1 row_rank, 1 batch_number union all
select 1 , 2, 1 union all
select 1 , 3, 2 union all
select 1 , 4, 2 union all
select 1 , 5, 3 union all
select 2 , 1, 4 union all
select 2 , 2, 4 union all
select 2 , 3, 5 union all
select 2 , 4, 5 union all
select 2 , 5, 6 union all
select 3 , 1, 7 union all
select 3 , 2, 7 union all
select 3 , 3, 8 union all
select 3 , 4, 8 union all
select 3 , 5, 9 union all
select 4 , 1, 10 union all
select 4 , 2, 10 union all
select 4 , 3, 11 union all
select 4 , 4, 11 union all
select 4 , 5, 12
As the split size is 2,
first two rows with in the 1st group_rank gets 1st batch number,
third fourth rows with in 1st group_rank gets 2nd batch number,
fifth row with in 1st group_rank gets 3rd batch number,
first two rows with in the 2nd group_rank gets 4th batch number,
third fourth rows with in 2nd group_rank gets 5th batch number,
fifth row gets with in 3rd group_rank gets 6th batch number
... and so on ..
As, I vary the split size,,, the batch number should grow or shrink accordingly.
Please provide me sql server TSQL query to do this.
Thanks,
Sounds like simple math to me, with a little 0-based/1-based voodoo at least:
((row_number() over (order by group_rank, row_rank) - 1) / #batch_size) + 1
SQL Fiddle demo
What this does is:
row_number() over (order by group_rank, row_rank): get the row number ordered over the entire set, ordered first by group_rank then by row_rank
- 1: make the row numbers 0-based
/ #batch_size: divide by the "batch size" you want (integer division at its best)
+ 1: make the result 1-based to match your output

How to do select count(*) group by and select * at same time?

For example, I have table:
ID | Value
1 hi
1 yo
2 foo
2 bar
2 hehe
3 ha
6 gaga
I want my query to get ID, Value; meanwhile the returned set should be in the order of frequency count of each ID.
I tried the query below but don't know how to get the ID and Value column at the same time:
SELECT COUNT(*) FROM TABLE group by ID order by COUNT(*) desc;
The count number doesn't matter to me, I just need the data to be in such order.
Desire Result:
ID | Value
2 foo
2 bar
2 hehe
1 hi
1 yo
3 ha
6 gaga
As you can see because ID:2 appears most times(3 times), it's first on the list,
then ID:1(2 times) etc.
you can try this -
select id, value, count(*) over (partition by id) freq_count
from
(
select 2 as ID, 'foo' as value
from dual
union all
select 2, 'bar'
from dual
union all
select 2, 'hehe'
from dual
union all
select 1 , 'hi'
from dual
union all
select 1 , 'yo'
from dual
union all
select 3 , 'ha'
from dual
union all
select 6 , 'gaga'
from dual
)
order by 3 desc;
select t.id, t.value
from TABLE t
inner join
(
SELECT id, count(*) as cnt
FROM TABLE
group by ID
)
x on x.id = t.id
order by x.cnt desc
How about something like
SELECT t.ID,
t.Value,
c.Cnt
FROM TABLE t INNER JOIN
(
SELECT ID,
COUNT(*) Cnt
FROM TABLE
GROUP BY ID
) c ON t.ID = c.ID
ORDER BY c.Cnt DESC
SQL Fiddle DEMO
I see the question is already answered, but since the most obvious and most simple solution is missing, I'm posting it anyway. It doesn't use self joins nor subqueries:
SQL> create table t (id,value)
2 as
3 select 1, 'hi' from dual union all
4 select 1, 'yo' from dual union all
5 select 2, 'foo' from dual union all
6 select 2, 'bar' from dual union all
7 select 2, 'hehe' from dual union all
8 select 3, 'ha' from dual union all
9 select 6, 'gaga' from dual
10 /
Table created.
SQL> select id
2 , value
3 from t
4 order by count(*) over (partition by id) desc
5 /
ID VALU
---------- ----
2 bar
2 hehe
2 foo
1 yo
1 hi
6 gaga
3 ha
7 rows selected.