Oracle query to display count and start, end of sequence - sql

I know basics of Oracle and I am a java developer, I can do the following operation/task in java by fetching the data and iterate over it. But I would like to know that is there any way to show start and end of the sequence and the difference in between start and end using SQL(Oracle) query.
Let's say I have a table TB1 with a column seq which contains some sequential numbers
SEQ
------
1
2
3
7
8
9
14
19
20
Is there any way to display the sequence start, end and the count as follows.
Start | end | count
---------------------
1 3 3
7 9 3
14 14 1
19 20 2
Please give me some pointer if it is achievable or not.
Thanks in advance.

Yes. You could do it easily using TABIBITOSAN method.
SELECT MIN(seq)
,MAX(seq)
,count(*)
FROM (
SELECT seq
,seq - row_number() OVER (
ORDER BY seq
) grp
FROM t
)
GROUP BY grp
ORDER BY 1;
Demo

You should write a procedure, and loop in through cursor. Keep three temp variable, wherever there is break in seq(make sure you do Order by on Seq column), and insert the start,end and count into other table.
See similar example that may be helpful to you.

SQL> WITH cte_table (seq) AS (
2 SELECT 1 FROM dual UNION ALL
3 SELECT 2 FROM dual UNION ALL
4 SELECT 3 FROM dual UNION ALL
5 SELECT 7 FROM dual UNION ALL
6 SELECT 8 FROM dual UNION ALL
7 SELECT 9 FROM dual UNION ALL
8 SELECT 14 FROM dual UNION ALL
9 SELECT 19 FROM dual UNION ALL
10 SELECT 20 FROM dual),
11 table_ AS (
12 SELECT seq, seq - row_number() OVER (ORDER BY seq) grp FROM cte_table)
13 SELECT MIN(seq) "START",
14 MAX(seq) "END",
15 COUNT(*) "COUNT"
16 FROM table_
17 GROUP BY grp
18 ORDER BY 1;
Output:
START END COUNT
---------- ---------- ----------
1 3 3
7 9 3
14 14 1
19 20 2
Using your table, the query will be
WITH table_ AS (
SELECT seq, seq - row_number() OVER (ORDER BY seq) grp FROM tb1)
SELECT MIN(seq) "START",
MAX(seq) "END",
COUNT(*) "COUNT"
FROM table_
GROUP BY grp
ORDER BY 1;

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 logic to roll the value same level

I am facing issue is solving on oracle logic
I have below requirement, I have one table with Some Numbers and Corresponding ID. I want to write logic where few rows are rolled same No. Please let me know what is best way to solve this. I have used left outer join on same table but its giving me cross join.
NO ID
1 A
1 B
1 C
2 A
2 B
2 C
NO ID RoID
1 A C
1 B
2 A C
2 B
You could achieve your desired output using Analytic functions:
MAX() OVER()
LAG() OVER()
For example,
SQL> WITH sample_data AS(
2 SELECT 1 NO, 'A' ID FROM dual UNION ALL
3 SELECT 1 NO, 'B' ID FROM dual UNION ALL
4 SELECT 1 NO, 'C' ID FROM dual UNION ALL
5 SELECT 2 NO, 'A' ID FROM dual UNION ALL
6 SELECT 2 NO, 'B' ID FROM dual UNION ALL
7 SELECT 2 NO, 'C' ID FROM dual
8 )
9 -- end of sample_data mocking a real table
10 SELECT no,
11 id,
12 lag(rn) over(PARTITION BY NO ORDER BY ID DESC) rn
13 FROM
14 ( SELECT t.*, MAX(ID) OVER(PARTITION BY NO ORDER BY ID DESC) rn FROM sample_data t
15 )
16 WHERE ID <> rn
17 ORDER BY no,
18 id;
NO I R
---------- - -
1 A C
1 B
2 A C
2 B

How to reverse the string 'ab,cd,ef' to 'ef->cd->ab'

when I select the table from Oracle, I want to handle one col'val :
eg:
'ab,cd,ef' to 'ef->cd->ab';
'AB,BC' to 'BC->AB';
'ACNN,BBCCAC' to 'BBCCAC->ACNN';
'BBBDC,DCCX,FFF' to 'FFF->DCCX->BBBDC'
We have two tasks. The first is to tokenize the original strings. This is quite easy with regular expressions (although there are more performant approaches if you are dealing with large volumes). The second task is to re-assemble the tokens in reverse order; we can use the 11gR2 LISTAGG() function for this:
with tokens as (
select distinct col1, regexp_substr(col1, '[^,]+', 1, level) as tkn, level as rn
from t23
connect by level <= regexp_count (col1, '[,]') +1
)
select col1
, listagg(tkn, '->')
within group (order by rn desc) as rev_col1
from tokens
group by col1
/
Here is a SQL Fiddle.
You can do it with a mix of string split and string aggregation.
Using:
REGEXP_SUBSTR : To split the comma delimited string into rows
LISTAGG : To aggregate the values
You can have a look at this article to understand how string split works http://lalitkumarb.wordpress.com/2015/03/04/split-comma-delimited-strings-in-a-table-using-oracle-sql/
SQL> WITH DATA AS(
2 SELECT 1 ID, 'ab,cd,ef' text FROM dual UNION ALL
3 SELECT 2 ID, 'AB,BC' text FROM dual UNION ALL
4 SELECT 3 ID, 'ACNN,BBCCAC' text FROM dual
5 )
6 SELECT ID,
7 listagg(text, ',') WITHIN GROUP (
8 ORDER BY rn DESC) reversed_indices
9 FROM
10 (SELECT t.id,
11 rownum rn,
12 trim(regexp_substr(t.text, '[^,]+', 1, lines.COLUMN_VALUE)) text
13 FROM data t,
14 TABLE (CAST (MULTISET
15 (SELECT LEVEL FROM dual CONNECT BY LEVEL <= regexp_count(t.text, ',')+1
16 ) AS sys.odciNumberList ) ) lines
17 ORDER BY ID
18 )
19 GROUP BY ID
20 /
ID REVERSED_INDICES
---------- ------------------------------
1 ef,cd,ab
2 BC,AB
3 BBCCAC,ACNN
SQL>
Let's say your table looks like:
SQL> SELECT * FROM t;
ID TEXT
---------- ------------------------------
1 ab,cd,ef
2 AB,BC
3 ACNN,BBCCAC
4 word1,word2,word3
5 1,2,3
SQL>
Using the above query:
SQL> SELECT ID,
2 listagg(text, '-->') WITHIN GROUP (
3 ORDER BY rn DESC) reversed_indices
4 FROM
5 (SELECT t.id,
6 rownum rn,
7 trim(regexp_substr(t.text, '[^,]+', 1, lines.COLUMN_VALUE)) text
8 FROM t,
9 TABLE (CAST (MULTISET
10 (SELECT LEVEL FROM dual CONNECT BY LEVEL <= regexp_count(t.text, ',')+1
11 ) AS sys.odciNumberList ) ) lines
12 ORDER BY ID
13 )
14 GROUP BY ID
15 /
ID REVERSED_INDICES
---------- ------------------------------
1 ef-->cd-->ab
2 BC-->AB
3 BBCCAC-->ACNN
4 word3-->word2-->word1
5 3-->2-->1
SQL>

Group by values that are in sequence

I have some table like this
row chequeNo
1 15
2 19
3 20
4 35
5 16
and I need to get the result like this
row from to
1 15 16
2 19 20
3 35 35
so I need groups of chequeNo where values would be sequential without any interruptions. chequeNo is unique column. Additionally it should be done with one sql select query, because I haven't permissions to create any sql structures except select queries.
So is it possible?
Would be grateful for any help
You can use Aketi Jyuuzou's technique called Tabibitosan here:
SQL> create table mytable (id,chequeno)
2 as
3 select 1, 15 from dual union all
4 select 2, 19 from dual union all
5 select 3, 20 from dual union all
6 select 4, 35 from dual union all
7 select 5, 16 from dual
8 /
Table created.
SQL> with tabibitosan as
2 ( select chequeno
3 , chequeno - row_number() over (order by chequeno) grp
4 from mytable
5 )
6 select row_number() over (order by grp) "row"
7 , min(chequeno) "from"
8 , max(chequeno) "to"
9 from tabibitosan
10 group by grp
11 /
row from to
---------- ---------- ----------
1 15 16
2 19 20
3 35 35
3 rows selected.
Regards,
Rob.
This should work with Oracle 10 (only tested with Oracle 11)
select group_nr + 1,
min(chequeno) as start_value,
max(chequeno) as end_value
from (
select chequeno,
sum(group_change_flag) over (order by rn) as group_nr
from (
select row_number() over (order by chequeno) as rn,
chequeno,
case
when chequeno - lag(chequeno,1,chequeno) over (order by chequeno) <= 1 then 0
else 1
end as group_change_flag
from foo
) t1
) t2
group by group_nr
order by group_nr
(it should work with any DBMS supporting standard SQL windowing functions, e.g. PostgreSQL, DB2, SQL Server 2012)
Here is a "plain vanilla" approach:
SELECT T1.chequeNo, T2.chequeNo
FROM Table1 AS T1 INNER JOIN Table1 AS T2 ON T2.chequeNo >= T1.chequeNo
WHERE
NOT EXISTS (SELECT T0.chequeNo FROM Table1 T0 WHERE T0.chequeNo IN ((T1.chequeNo-1), (T2.chequeNo+1)))
AND (SELECT COUNT(*) FROM Table1 T0 WHERE T0.chequeNo BETWEEN T1.chequeNo AND T2.chequeNo)=(T2.chequeNo - T1.chequeNo + 1)
ORDER BY 1,2
Please let me know if it's too inefficient for large data sets.
CREATE TABLE YOUR_TABLE (
chequeNo NUMBER PRIMARY KEY
);
INSERT INTO YOUR_TABLE VALUES (15);
INSERT INTO YOUR_TABLE VALUES (19);
INSERT INTO YOUR_TABLE VALUES (20);
INSERT INTO YOUR_TABLE VALUES (35);
INSERT INTO YOUR_TABLE VALUES (16);
SELECT T1.chequeNo "from", T2.chequeNo "to"
FROM
(
SELECT chequeNo, ROW_NUMBER() OVER (ORDER BY chequeNo) RN
FROM (
SELECT chequeNo, LAG(chequeNo) OVER (ORDER BY chequeNo) PREV
FROM YOUR_TABLE
)
WHERE PREV IS NULL OR chequeNo > PREV + 1
) T1
JOIN
(
SELECT chequeNo, ROW_NUMBER() OVER (ORDER BY chequeNo) RN
FROM (
SELECT chequeNo, LEAD(chequeNo) OVER (ORDER BY chequeNo) NEXT
FROM YOUR_TABLE
)
WHERE NEXT IS NULL OR chequeNo < NEXT - 1
) T2
USING (RN);
Result:
from to
---------------------- ----------------------
15 16
19 20
35 35
If we spice things up a little...
INSERT INTO YOUR_TABLE VALUES (17);
INSERT INTO YOUR_TABLE VALUES (18);
...we get:
from to
---------------------- ----------------------
15 20
35 35