How to combine 2 select statements in oracle - oracle

i have table test2.it contains
ID
1
4
5
10
now i found missing numbers in this sequence.with this query
SELECT min_ID - 1 + level mn FROM
( SELECT MIN(ID) min_ID , MAX(ID) max_ID FROM test2 )
CONNECT BY level <= max_ID - min_ID + 1 minus SELECT ID FROM test2
output is:
MN
---
2
3
6
7
8
9
now i want to combine these 2 columns.I am unable to do this please help me.
i want output like
1 2
4 3
7 5
10 6
8
9

Oracle Setup:
CREATE TABLE test2 (id) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 4 FROM DUAL UNION ALL
SELECT 5 FROM DUAL UNION ALL
SELECT 10 FROM DUAL;
Query:
WITH bounds ( mn, mx ) AS (
SELECT MIN( id ), MAX( id ) FROM test2
),
missing (id, rn) AS (
SELECT id, ROWNUM
FROM (
SELECT mn + LEVEL AS id
FROM bounds
CONNECT BY LEVEL < MX - MN
MINUS
SELECT id
FROM test2
)
),
existing ( id, rn ) AS (
SELECT id, ROWNUM
FROM test2
)
SELECT e.id, m.id
FROM existing e
FULL OUTER JOIN
missing m
ON ( e.rn = m.rn );
Output
ID ID
---------- ----------
1 2
4 3
5 6
10 7
9
8

Related

Requirement in Oracle PL/SQL to update labels

I have 2 tables - tab1 , tab2 with following data
tab1 data:
OID Label
1 MX1
1 MX2
1 MX3
2 MX4
2 MX5
tab2 data:
OID ID Label
1 5678
1 2347
1 9687
2 4567
2 3455
The join condition between these two tables is oid column.I need to create a process which will update Label column from tab1 to Label column of tab2.It doesn't matter which label gets assigned to which record of tab2 for a particular oid. The only check that needs to happen is that both the tables should have same number of records for an oid.The final outcome should be the following
tab2 data:
OID ID Label
1 5678 MX1
1 2347 MX2
1 9687 MX3
2 4567 MX4
2 3455 MX5
Again, it doesn't matter which label gets assigned to tab2 for a particular oid,but the same label cannot be repeated for a particular oid.What would be the best way to write a code for this requirement?
Here is a sql solution:
merge into tab2
using
(
select t2."id" as ide,t1."label" labela from
(select rownum n,"label","oid" from tab1 order by "oid")t1,
(select rownum n, a2.* from tab2 a2 order by "oid")t2
where t1.n=t2.n and
t1."oid"=t2."oid"
) tb4
on (tab2."id" = tb4.ide)
when matched then
update set tab2."label" = tb4.labela;
Result:
oid| id | label
-----------------
1 5678 mx1
1 2347 mx2
1 9687 mx3
2 4567 mx4
2 3455 mx5
Sample tables:
SQL> select * from tab1 order by oid, label;
OID LAB
---------- ---
1 mx1
1 mx2
1 mx3
2 mx4
2 mx5
SQL> select * from tab2 order by oid, id;
OID ID LAB
---------- ---------- ---
1 2347
1 5678
1 9687
2 3455
2 4567
SQL>
This is query that returns desired result:
SQL> with
2 t1 as (select oid, label, rowid rwid,
3 row_number() over (partition by oid order by label) rn
4 from tab1
5 ),
6 t2 as (select oid, id, rowid rwid,
7 row_number() over (partition by oid order by id) rn
8 from tab2
9 )
10 select b.oid, b.id, a.label
11 from t1 a join t2 b on a.oid = b.oid and a.rn = b.rn;
OID ID LAB
---------- ---------- ---
1 2347 mx1
1 5678 mx2
1 9687 mx3
2 3455 mx4
2 4567 mx5
SQL>
A few options I tried: correlated update won't work because of
ORA-01779: cannot modify a column which maps to a non key-preserved table
SQL> update (
2 with
3 t1 as (select oid, label, rowid rwid,
4 row_number() over (partition by oid order by label) rn
5 from tab1
6 ),
7 t2 as (select oid, id, rowid rwid, label,
8 row_number() over (partition by oid order by id) rn
9 from tab2
10 )
11 select b.oid, b.id, b.label b_label, a.label a_label
12 from t1 a join t2 b on a.oid = b.oid and a.rn = b.rn
13 )
14 set b_label = a_label;
set b_label = a_label
*
ERROR at line 14:
ORA-01779: cannot modify a column which maps to a non key-preserved table
SQL>
MERGE won't work because of
ORA-01732: data manipulation operation not legal on this view
SQL> merge into
2 (select oid, id, label, row_Number() over (partition by oid order by id ) rn from tab2) b
3 using (select oid, label, row_number() over (partition by oid order by label) rn from tab1) a
4 on (a.oid = b.oid and
5 a.rn = b.rn)
6 when matched then update set
7 b.label = a.label;
(select oid, id, label, row_Number() over (partition by oid order by id ) rn from tab2) b
*
ERROR at line 2:
ORA-01732: data manipulation operation not legal on this view
SQL>
Merge would accept a view (created with create view ...), but a view has to be updateable; this one can't be because it contains analytic function.
What's left is a PL/SQL procedure:
SQL> begin
2 for cur_r in (with
3 t1 as (select oid, label, rowid rwid,
4 row_number() over (partition by oid order by label) rn
5 from tab1
6 ),
7 t2 as (select oid, id, rowid rwid,
8 row_number() over (partition by oid order by id) rn
9 from tab2
10 )
11 select b.rwid, a.label
12 from t1 a join t2 b on a.oid = b.oid and a.rn = b.rn
13 )
14 loop
15 update tab2 b set
16 b.label = cur_r.label
17 where b.rowid = cur_r.rwid;
18 end loop;
19 end;
20 /
PL/SQL procedure successfully completed.
SQL> select * from tab2 order by oid, id;
OID ID LAB
---------- ---------- ---
1 2347 mx1
1 5678 mx2
1 9687 mx3
2 3455 mx4
2 4567 mx5
SQL>
Maybe someone has another idea; I'd like to see it & learn something new.

Sum 2 columns from multiple rows with same ID

Currently, I have a table MY_TABLE like below:
ID ACCT_TYPE CREDIT_AMT DEBIT_AMT
-- --------- ---------- ---------
1 CDT_01 4 (null)
1 DBT_01 (null) 6
One ID can have multiple ACCT_TYPE like above, and each type has its own amount.
I want to just select the row which has ACCT_TYPE like 'CDT_%' but also the total_amount column which is the total of credit_amt and debit_amt column for the same ID.
My expected output like below:
ID ACCT_TYPE TOTAL_AMT
-- --------- ---------
1 CDT_01 10
I tried with this select statement below but it's no use, I think it's because of different ACCT_TYPE:
Select ID, ACCT_TYPE, SUM(NVL(CREDIT_AMT, 0) + NVL(DEBIT_AMT, 0)) TOTAL_AMT
FROM MY_TABLE WHERE ACCT_TYPE LIKE 'CDT_%' GROUP BY ID, ACCT_TYPE;
Here is the output of the select statement above:
ID ACCT_TYPE TOTAL_AMT
-- --------- ---------
1 CDT_01 4
I just begin to learn some query so I don't know is it really possible to get my expected output.
One way to do it is like below:
with inputs (ID, ACCT_TYPE, CREDIT_AMT, DEBIT_AMT) as
(
select 1, 'CDT_01', 4, null from dual union all
select 1, 'DBT_01', null, 6 from dual
),
prep as
(
select t.*, sum(nvl(credit_amt,0)) over (partition by id) + sum(nvl(debit_amt,0)) over (partition by id) as sum_per_id
from inputs t
)
select id, acct_type, sum_per_id
from prep
where acct_type like 'CDT_%';
Output:
A correlated subquery might be one option; sample data (thank you, #Ranagal) in lines #1 - 5; query that does the job begins at line #6.
SQL> with inputs (ID, ACCT_TYPE, CREDIT_AMT, DEBIT_AMT) as
2 (
3 select 1, 'CDT_01', 4, null from dual union all
4 select 1, 'DBT_01', null, 6 from dual
5 )
6 select a.id,
7 a.acct_type,
8 (select sum(nvl(b.credit_amt, 0)) +
9 sum(nvl(b.debit_amt , 0))
10 from inputs b
11 where b.id = a.id
12 ) total_amt
13 from inputs a
14 where acct_type like 'CDT%';
ID ACCT_T TOTAL_AMT
---------- ------ ----------
1 CDT_01 10
SQL>

Count column comma delimited values oracle

Is it possible to count and also group by comma delimited values in the oracle database table? This is a table data example:
id | user | title |
1 | foo | a,b,c |
2 | bar | a,d |
3 | tee | b |
The expected result would be:
title | count
a | 2
b | 2
c | 1
d | 1
I wanted to use concat like this:
SELECT a.title FROM Account a WHERE concat(',', a.title, ',') LIKE 'a' OR concat(',', a.title, ',') LIKE 'b' ... GROUP BY a.title?
But I'm getting invalid number of arguments on concat. The title values are predefined, therefore I don't mind if I have to list all of them in the query. Any help is greatly appreciated.
This uses simple string functions and a recursive sub-query factoring and may be faster than using regular expressions and correlated joins:
Oracle Setup:
CREATE TABLE account ( id, "user", title ) AS
SELECT 1, 'foo', 'a,b,c' FROM DUAL UNION ALL
SELECT 2, 'bar', 'a,d' FROM DUAL UNION ALL
SELECT 3, 'tee', 'b' FROM DUAL;
Query:
WITH positions ( title, start_pos, end_pos ) AS (
SELECT title,
1,
INSTR( title, ',', 1 )
FROM account
UNION ALL
SELECT title,
end_pos + 1,
INSTR( title, ',', end_pos + 1 )
FROM positions
WHERE end_pos > 0
),
items ( item ) AS (
SELECT CASE end_pos
WHEN 0
THEN SUBSTR( title, start_pos )
ELSE SUBSTR( title, start_pos, end_pos - start_pos )
END
FROM positions
)
SELECT item,
COUNT(*)
FROM items
GROUP BY item
ORDER BY item;
Output:
ITEM | COUNT(*)
:--- | -------:
a | 2
b | 2
c | 1
d | 1
db<>fiddle here
Split titles to rows and count them.
SQL> with test (id, title) as
2 (select 1, 'a,b,c' from dual union all
3 select 2, 'a,d' from dual union all
4 select 3, 'b' from dual
5 ),
6 temp as
7 (select regexp_substr(title, '[^,]', 1, column_value) val
8 from test cross join table(cast(multiset(select level from dual
9 connect by level <= regexp_count(title, ',') + 1
10 ) as sys.odcinumberlist))
11 )
12 select val as title,
13 count(*)
14 From temp
15 group by val
16 order by val;
TITLE COUNT(*)
-------------------- ----------
a 2
b 2
c 1
d 1
SQL>
If titles aren't that simple, then modify REGEXP_SUBSTR (add + sign) in line #7, e.g.
SQL> with test (id, title) as
2 (select 1, 'Robin Hood,Avatar,Star Wars Episode III' from dual union all
3 select 2, 'Mickey Mouse,Avatar' from dual union all
4 select 3, 'The Godfather' from dual
5 ),
6 temp as
7 (select regexp_substr(title, '[^,]+', 1, column_value) val
8 from test cross join table(cast(multiset(select level from dual
9 connect by level <= regexp_count(title, ',') + 1
10 ) as sys.odcinumberlist))
11 )
12 select val as title,
13 count(*)
14 From temp
15 group by val
16 order by val;
TITLE COUNT(*)
------------------------------ ----------
Avatar 2
Mickey Mouse 1
Robin Hood 1
Star Wars Episode III 1
The Godfather 1
SQL>

Oracle Hierarchical Query output format

I have a table that list all the security project access numbers that you can possibly have for a given user. I am using Oracle db,
Security
---------
8055
8055.01
8056
8056.00
8056.01
8056.01.01
9005.01.02
9876.03
I am trying to get an output table in a format useful for our treeview application
Security Parent ID Group ID Level No
------------------------------------------------
8055 null 1 1
8055.01 8055 1 2
8056 null 2 1
8056.00 8056 2 2
8056.01 8056 2 2
8056.01.01 8056.01 2 3
9005.01.02 null 3 1
9876.03 null 4 1
can any one help me to get the above output format ?
Oracle Setup:
CREATE TABLE Table_name ( Security ) AS
SELECT '8055' FROM DUAL UNION ALL
SELECT '8055.01' FROM DUAL UNION ALL
SELECT '8056' FROM DUAL UNION ALL
SELECT '8056.00' FROM DUAL UNION ALL
SELECT '8056.01' FROM DUAL UNION ALL
SELECT '8056.01.01' FROM DUAL UNION ALL
SELECT '9005.01.02' FROM DUAL UNION ALL
SELECT '9876.03' FROM DUAL;
Query:
WITH parents ( security, parent_id ) AS (
SELECT security,
SUBSTR( security, 1, INSTR( security, '.', -1 ) - 1 )
FROM table_name
)
SELECT security,
PRIOR security AS parent_id,
DENSE_RANK() OVER ( ORDER BY CONNECT_BY_ROOT( security ) ) AS group_id,
LEVEL AS level_no
FROM parents p
START WITH NOT EXISTS ( SELECT 'X'
FROM table_name t
WHERE t.security = p.parent_id )
CONNECT BY PRIOR security = parent_id
ORDER BY security;
Output:
SECURITY PARENT_ID GROUP_ID LEVEL_NO
---------- ---------- ---------- ----------
8055 1 1
8055.01 8055 1 2
8056 2 1
8056.00 8056 2 2
8056.01 8056 2 2
8056.01.01 8056.01 2 3
9005.01.02 3 1
9876.03 4 1

scalar subquery has an aggregate operation

My oracle version is 10.2.
It's very strange when a scalar subquery has an aggregate operation.
my table named t_test looked like this;
t_id t_name
1 1
2 1
3 2
4 2
5 3
6 3
query string looked like this;
select t1.t_id,
(select count(t_name)
from (select t2.t_name
from t_test t2
where t2.t_id=t1.t_id
group by t2.t_name)) a
from t_test t1
this query's result is,
t_id a
1 3
2 3
3 3
4 3
5 3
6 3
which is very weird,
take t1.t_id=1 for example,
select count(t_name)
from (select t2.t_name
from t_test t2
where t2.t_id=1
group by t2.t_name)
the result is 1,
somehow,the 'where' operator doesn't work,the result is exactly the same as I put my query like this:
select t1.t_id,
(select count(t_name)
from (select t2.t_name
from t_test t2
group by t2.t_name)) a
from t_test t1
why?
Can you post a cut-and-paste from SQL*Plus showing exactly what query you're running? The query you posted does not appear to be valid-- the alias t1 is not going to be valid in the subquery where you're referencing it. That makes me suspect that you're simplifying the problem to post here but you've accidentally left something important out.
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select 1 id, 1 name from dual union all
3 select 2,1 from dual union all
4 select 3,2 from dual union all
5 select 4,2 from dual union all
6 select 5,3 from dual union all
7 select 6,3 from dual
8 )
9 select t1.id
10 ,(select count(b.name)
11 from (select t2.name
12 from x t2
13 where t2.id = t1.id
14 group by t2.name) b) a
15* from x t1
SQL> /
where t2.id = t1.id
*
ERROR at line 13:
ORA-00904: "T1"."ID": invalid identifier
Presumably, it would be much more natural to write the query like this (assuming you really want to use a scalar subquery) where t1 is going to be a valid alias in the scalar subquery.
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select 1 id, 1 name from dual union all
3 select 2,1 from dual union all
4 select 3,2 from dual union all
5 select 4,2 from dual union all
6 select 5,3 from dual union all
7 select 6,3 from dual
8 )
9 select t1.id
10 ,(select count(t2.name)
11 from x t2
12 where t2.id = t1.id) cnt
13* from x t1
SQL> /
ID CNT
---------- ----------
1 1
2 1
3 1
4 1
5 1
6 1
6 rows selected.

Resources