SQL of group by in order by - sql

[Raw data]
A B C
1 10 1
1 10 2
2 20 3
2 20 4
1 100 5
1 100 6
[Wanted result]
A SUM_OF_B
1 20
2 40
1 200
It's unuseful that the query has the simple 'group by' clause and 'dense_rank over partition by' because grouping works all rows. However I want grouping in state of ordering. How do I write the proper query?

You need to identify the groups of adjacent records. You can actually do this by using a difference of row numbers approach -- assuming that c orders the rows. The difference is constant for consecutive values of a that are the same:
select a, sum(b)
from (select t.*,
(row_number() over (order by c) -
row_number() over (partition by a order by c)
) as grp
from table t
) t
group by grp, a
order by min(c);

Related

SQL query to partition rows into groups where lag (difference between rows) is greater than some value

Suppose I have a table like
id
1
3
4
10
12
19
and I'd like to group the ids (in sorted order) into the same group if they differ by 5 or less, and a new group if they differ by 6 or more. So the output would be:
id
group
1
1
3
1
4
1
10
2
12
2
19
3
Is this possible in SQL? It will be a query in Trino, and I see they have commands like lag and partition. Has anyone made a query like this that can help out?
You can use a cte with lead:
with cte(id, l1) as (
select t.id, abs(coalesce(lead(t.id) over (order by t.id), 0) - t.id) < 6 from tbl t
)
select c.id, (select sum(c1.id < c.id and c1.l1 = 0) from cte c1) + 1 from cte c

(SQL) Per ID, starting from the first row, return all successive rows with a value N greater than the prior returned row

I have the following example dataset:
ID
Value
Row index (for reference purposes only, does not need to exist in final output)
a
4
1
a
7
2
a
12
3
a
12
4
a
13
5
b
1
6
b
2
7
b
3
8
b
4
9
b
5
10
I would like to write a SQL script that returns the next row which has a Value of N or more than the previously returned row starting from the first row per ID and ordered ascending by [Value]. An example of the final table for N = 3 should look like the following:
ID
Value
Row index
a
4
1
a
7
2
a
12
3
b
1
6
b
4
9
Can this script be written in a vectorised manner? Or must a loop be utilised? Any advice would be greatly appreciated. Thanks!
SQL tables represent unordered sets. There is no definition of "previous" value, unless you have a column that specifies the ordering. With such a column, you can use lag():
select t.*
from (select t.*,
lag(value) over (partition by id order by <ordering column>) as prev_value
from t
) t
where prev_value is null or prev_value <= value - 3;
EDIT:
I think I misunderstood what you want to do. You seem to want to start with the first row for each id. Then get the next row that is 3 or higher in value. Then hold onto that value and get the next that is 3 or higher than that. And so on.
You can do this in SQL using a recursive CTE:
with ts as (
select distinct t.id, t.value, dense_rank() over (partition by id order by value) as seqnum
from t
),
cte as (
select id, value, value as grp_value, 1 as within_seqnum, seqnum
from ts
where seqnum = 1
union all
select ts.id, ts.value,
(case when ts.value >= cte.grp_value + 3 then ts.value else cte.grp_value end),
(case when ts.value >= cte.grp_value + 3 then 1 else cte.within_seqnum + 1 end),
ts.seqnum
from cte join
ts
on ts.id = cte.id and ts.seqnum = cte.seqnum + 1
)
select *
from cte
where within_seqnum = 1
order by id, value;
Here is a db<>fiddle.

SQL query to find counts of numbers in running total

Suppose the table has 1 column ID and the values are as below:
ID
5
5
5
6
5
5
6
6
the output should be
ID count
5 3
6 1
5 2
6 2
How can we do that in a single SQL query.
If you want to find the Total count of the Records you have you can write like
select count(*) from database_name order by column_name;
In relational databases data in the table has no any order, see this: https://en.wikipedia.org/wiki/Table_(database)
the database system does not guarantee any ordering of the rows unless
an ORDER BY clause is specified in the SELECT statement that queries
the table.
therefore, in order to get desired results, you must have an additional colum in the table that defines an order of rows (and can by used in ORDER BY clause).
In the below examle cn column defines such an order:
select * from tab123 ORDER BY rn;
RN ID
---------- -------
1 5
2 5
3 5
4 6
5 5
6 5
7 6
8 6
Starting from Oracle version 12c new MATCH_REGOGNIZE clause can be used:
select * from tab123
match_recognize(
order by rn
measures
strt.id as id,
count(*) as cnt
one row per match
after match skip past last row
pattern( strt ss* )
define ss as ss.id = prev( ss.id )
);
On earlier versions that support windows function (Oracle 10 and above) you can use two windows functions: LAG ... over and SUM ... over, in this way
select max( id ) as id, count(*) as cnt
FROM (
select id, sum( xxx ) over (order by rn ) as yyy
from (
select t.*,
case lag( id ) over (order by rn )
when id then 0 else 1 end as xxx
from tab123 t
)
)
GROUP BY yyy
ORDER BY yyy;

Query to group based on the sorted table result

Below is my table
a 1
a 2
a 1
b 1
a 2
a 2
b 3
b 2
a 1
My Expected output is
a 4
b 1
a 4
b 5
a 1
I want them to be grouped if they are in sequence.
If your dbms supports window functions, you can use the row_number difference to assign the same group to consecutive values (which are the same) in one column. After assigning the groups, it is easy to sum the values for each group.
select col1,sum(col2)
from (select t.*,
row_number() over(order by someid)
- row_number() over(partition by col1 order by someid) as grp
from tablename t
) x
group by col1,grp
Replace tablename, col1,col2,someid with the appropriate column names. someid should be the column to be ordered by.

SQL Local Minima and Maxima

I have this data:
row_id type value
1 a 1
2 a 2
3 a 3
4 a 5 --note that type a, value 4 is missing
5 a 6
6 a 7
7 b 1
8 b 2
9 b 3
10 b 4
11 b 5 --note that type b is missing no values from 1 to 5
12 c 1
13 c 3 --note that type c, value 2 is missing
I want to find the minimum and maximum values for each consecutive "run" within each type. That is, I want to return
row_id type group_num min_value max_value
1 a 1 1 3
2 a 2 5 7
3 b 1 1 5
4 c 1 1 1
5 c 2 3 3
I am a fairly experienced SQL user, but I've never solved this problem. Obviously I know how to get the overall minimum and maximum for each type, using GROUP, MIN, and MAX, but I'm really at a loss for these local minima and maxima. I haven't found anything on other questions that answers my question.
I'm using PLSQL Developer with Oracle 11g. Thanks!
This is a gaps-and-islands problem. You can use an analytic function effect/trick to finds the chains of contiguous values for each type:
select type,
min(value) as min_value,
max(value) as max_value
from (
select type, value,
dense_rank() over (partition by type order by value)
- dense_rank() over (partition by null order by value) as chain
from your_table
)
group by type, chain
order by type, min(value);
The inner query uses the difference between the ranking of the values within the type and within the entire result set to create the 'chain' number. The outer query just uses that for the grouping.
SQL Fiddle including the result of the inner query.
This is one way to achieve the result you require:
with step_1 as (
select w.type,
w.value,
w.value - row_number() over (partition by w.type order by w.row_id) as grp
from window_test w
), step_2 as (
select x.type,
x.value,
dense_rank() over (partition by x.type order by x.grp) as grp
from step_1 x
)
select rank() over (order by y.type, y.grp) as row_id,
y.type,
y.grp as group_num,
min(y.value) as min_val,
max(y.value) as max_val
from step_2 y
group by y.type, y.grp
order by 1;