Retrieve records from a specific column in oracle - sql

S.NO id Pid
1 123 PAQ123
2 433 WSD3FF
3 565 PAS45E
4 123 PAQ123X
5 433 WSD3FFY
6 123 PAQ123Z
suppose the above is the sample records in the database.
Now I want to find out in the database whether there is any word (example PAQ123) which is repeating with some prefixes/suffixes like in (PAQ123X,PAQ123Z).
How can I write a query which would result into the above list scenario?

Oracle Setup:
CREATE TABLE table_name ( S_NO, id, Pid ) AS
SELECT 1, 123, 'PAQ123' FROM DUAL UNION ALL
SELECT 2, 433, 'WSD3FF' FROM DUAL UNION ALL
SELECT 3, 565, 'PAS45E' FROM DUAL UNION ALL
SELECT 4, 123, 'PAQ123X' FROM DUAL UNION ALL
SELECT 5, 433, 'WSD3FFY' FROM DUAL UNION ALL
SELECT 6, 123, 'PAQ123Z' FROM DUAL;
Query:
SELECT *
FROM (
SELECT t.*,
( SELECT COUNT(*)
FROM table_name x
WHERE t.id = x.id
AND LENGTH( t.Pid ) < LENGTH( x.pid )
AND INSTR( x.Pid, t.Pid ) = 1 ) AS num_matches
FROM Table_name t
)
WHERE num_matches > 0;
Output:
S_NO ID PID NUM_MATCHES
---------- ---------- ------- -----------
1 123 PAQ123 2
2 433 WSD3FF 1
If you want to get the matches then you can use a collection:
CREATE TYPE stringlist AS TABLE OF VARCHAR2(100);
/
Query:
SELECT *
FROM (
SELECT t.*,
CAST(
MULTISET(
SELECT PID
FROM table_name x
WHERE t.id = x.id
AND LENGTH( t.Pid ) < LENGTH( x.pid )
AND INSTR( x.Pid, t.Pid ) = 1
)
AS stringlist
) AS matches
FROM Table_name t
)
WHERE matches IS NOT EMPTY;
or (since I'm not sure MULTISET is in 10g):
SELECT *
FROM (
SELECT t.*,
CAST(
(
SELECT COLLECT( PID )
FROM table_name x
WHERE t.id = x.id
AND LENGTH( t.Pid ) < LENGTH( x.pid )
AND INSTR( x.Pid, t.Pid ) = 1
)
AS stringlist
) AS matches
FROM Table_name t
)
WHERE matches IS NOT EMPTY;
Output
S_NO ID PID MATCHES
---------- ---------- ------- ------------------------------------
1 123 PAQ123 TEST.STRINGLIST('PAQ123X','PAQ123Z')
2 433 WSD3FF TEST.STRINGLIST('WSD3FFY')

Related

Select numbers where not exist with a specific code

I want to select all numbers where COD = 'GV' but also select the numbers that do not exist with code 'GV'... number 4170 for example...
COD NUM
---- ----
GV 4168
SERV 4168
GV 4169
SERV 4169
SERV 4170
SERV 4171
GV 4171
Literally, transcribed what you asked for:
-- numbers for COD = GV
select num
from your_table
where cod = 'GV'
union
-- numbers for COD <> GV
select num
from your_table
where cod <> 'GV
Simplified:
select distinct num
from your_table;
as you, basically, want all numbers, regardless of the COD value.
You can use a partitioned outer join:
WITH bounds ( min_num, max_num ) AS (
SELECT MIN( num ),
MAX( num )
FROM test_data
)
SELECT t.COD,
n.NUM
FROM (
SELECT min_num + LEVEL - 1 AS num
FROM bounds
CONNECT BY LEVEL <= max_num - min_num + 1
) n
LEFT OUTER JOIN test_data t
PARTITION BY ( t.COD )
ON ( n.NUM = t.NUM )
WHERE t.NUM IS NULL
AND t.COD = 'GV';
Which, for your sample data:
CREATE TABLE test_data ( COD, NUM ) AS
SELECT 'GV', 4168 FROM DUAL UNION ALL
SELECT 'SERV', 4168 FROM DUAL UNION ALL
SELECT 'GV', 4169 FROM DUAL UNION ALL
SELECT 'SERV', 4169 FROM DUAL UNION ALL
SELECT 'SERV', 4170 FROM DUAL UNION ALL
SELECT 'SERV', 4171 FROM DUAL UNION ALL
SELECT 'GV', 4171 FROM DUAL;
Outputs:
COD | NUM
:-- | ---:
GV | 4170
db<>fiddle here

How to arrange/sort string in Oracle SQL - 11g

I have a string an string "ADBDkK" and I need to sort it as "ABDDKk", like Arrays.sort() in java. I know it can be done by using PL/SQL but I need of this in Oracle SQL statement.
Input:
ADBDkK
ZXYABC
Output:
ABDDKk
ABCXYZ
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE test ( value ) AS
SELECT 'ADBDkK' FROM DUAL UNION ALL
SELECT 'ZXYABC' FROM DUAL;
Query 1:
WITH chars ( id, value, ch, lvl ) AS (
SELECT ROWNUM, value, SUBSTR( value, 1, 1 ), 1
FROM test
UNION ALL
SELECT id, value, SUBSTR( value, lvl+1, 1 ), lvl+1
FROM chars
WHERE lvl < LENGTH( value )
)
SELECT LISTAGG( ch ) WITHIN GROUP ( ORDER BY ch ) AS value
FROM chars
GROUP BY id
ORDER BY id
Results:
| VALUE |
|--------|
| ABDDKk |
| ABCXYZ |
Query 2:
SELECT LISTAGG( COLUMN_VALUE )
WITHIN GROUP ( ORDER BY COLUMN_VALUE ) AS value
FROM (
SELECT value,
ROWNUM AS id
FROM test
) t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT SUBSTR( t.value, LEVEL, 1 )
FROM DUAL
CONNECT BY LEVEL <= LENGTH( t.value )
)
AS SYS.ODCIVARCHAR2LIST
)
) c
GROUP BY t.id
ORDER BY t.id
Results:
| VALUE |
|--------|
| ABDDKk |
| ABCXYZ |
For a single string:
select listagg(regexp_substr('ADBDkK', '\w', 1 ,level),'')
within group (order by 1) from dual
connect by regexp_substr('ADBDkK', '\w', 1 ,level) is not null;
A slightly different way using a function:
CREATE OR REPLACE FUNCTION sort_string(my_string IN VARCHAR2)
RETURN VARCHAR2 IS
ret_string VARCHAR2(4000);
BEGIN
SELECT LISTAGG(regexp_substr(my_string, '\w', 1, level), '') WITHIN
GROUP(
ORDER BY 1)
INTO ret_string
FROM dual
CONNECT BY regexp_substr(my_string, '\w', 1, level) IS NOT NULL;
RETURN ret_string;
END;
Then from sqlplus:
SQL> select sort_string('ADBDkK') as RESULT from dual;
RESULT
------
ABDDKk
SQL> select sort_string('ZXYABC') as RESULT from dual;
RESULT
------
ABCXYZ
with t1 as (
select 'London Singapur' tmp from dual union all
select 'Singapur China' tmp from dual union all
select 'USA JAPAN ' tmp from dual union all
select 'JAPAN USA' tmp from dual union all
select 'Singapur London' tmp from dual
),
dst as ( select ROWNUM rwn,tmp,REGEXP_COUNT(tmp,'[^[:space:]]+') cnt_array from t1 ),
rc(id, cnt_id, tmp, CNT_ARRAY, SL) AS (
SELECT rwn id,1 cnt_id,tmp, CNT_ARRAY, regexp_substr(tmp,'[^[:space:]]+',1,1) sl FROM dst
UNION ALL
SELECT rc.id, rc.cnt_id+1 cnt_id, rc.tmp, rc.CNT_ARRAY, regexp_substr(rc.tmp,'[^[:space:]]+',1,rc.cnt_id+1) sl
FROM dst tr, rc
WHERE tr.rwn = rc.id and rc.cnt_id+1<=tr.CNT_ARRAY
),
srt as (select rc.*,row_number() over (partition by id order by id) nb,
listagg(sl,' ') WITHIN GROUP (ORDER BY sl) over(partition by id) fiel_srt
from rc)
select * from srt where nb=1

Oracle/SQL - Need query that will select max value from string in each row

I need a graceful way to select the max value from a field holding a comma delimited list.
Expected Values:
List_1 | Last
------ | ------
A,B,C | C
B,D,C | D
I'm using the following query and I'm not getting what's expected.
select
list_1,
(
select max(values) WITHIN GROUP (order by 1)
from (
select
regexp_substr(list_1,'[^,]+', 1, level) as values
from dual
connect by regexp_substr(list_1, '[^,]+', 1, level) is not null)
) as last
from my_table
Anyone have any ideas to fix my query?
with
test_data ( id, list_1 ) as (
select 101, 'A,B,C' from dual union all
select 102, 'B,D,C' from dual union all
select 105, null from dual union all
select 122, 'A' from dual union all
select 140, 'A,B,B' from dual
)
-- end of simulated table (for testing purposes only, not part of the solution)
select id, list_1, max(token) as max_value
from ( select id, list_1,
regexp_substr(list_1, '([^,])(,|$)', 1, level, null, 1) as token
from test_data
connect by level <= 1 + regexp_count(list_1, ',')
and prior id = id
and prior sys_guid() is not null
)
group by id, list_1
order by id
;
ID LIST_1_ MAX_VAL
---- ------- -------
101 A,B,C C
102 B,D,C D
105
122 A A
140 A,B,B B
In Oracle 12.1 or higher, this can be re-written using the LATERAL clause:
select d.id, d.list_1, x.max_value
from test_data d,
lateral ( select max(regexp_substr(list_1, '([^,]*)(,|$)',
1, level, null, 1)) as max_value
from test_data x
where x.id = d.id
connect by level <= 1 + regexp_count(list_1, ',')
) x
order by d.id
;

How to replace comma separated text values in a column in Oracle?

I have a table t1 with a varchar col V_RELNIST_SKEY which contains comma separated numbers between 1 and 12 as shown. I want to write a select statement to replace numbers by string. For e.g., value 5,6 should be replaced by five,six and so on.
|V_RELNIST_SKEY|
|6 |
|5,6 |
|1,12 |
|1,2,3,12 |
Oracle Setup:
CREATE TABLE test_data ( value ) as
SELECT '9' FROM DUAL UNION ALL
SELECT '6' FROM DUAL UNION ALL
SELECT '1' FROM DUAL UNION ALL
SELECT '2,3' FROM DUAL UNION ALL
SELECT '5,6,7' FROM DUAL UNION ALL
SELECT '8,4' FROM DUAL UNION ALL
SELECT '1,2,3,4,5,6,7,8,9,10,11,12' FROM DUAL;
Query:
SELECT value,
column_value AS words
FROM test_data t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT LISTAGG(
TO_CHAR(
TO_DATE(
REGEXP_SUBSTR( t.value, '\d+', 1, LEVEL ),
'J'
),
'JSP'
),
','
) WITHIN GROUP ( ORDER BY LEVEL )
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.value, '\d+' )
) AS SYS.ODCIVARCHAR2LIST
)
) w;
Output:
VALUE WORDS
-------------------------- ----------------------------------------
9 NINE
6 SIX
1 ONE
2,3 TWO,THREE
5,6,7 FIVE,SIX,SEVEN
8,4 EIGHT,FOUR
1,2,3,4,5,6,7,8,9,10,11,12 ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,
NINE,TEN,ELEVEN,TWELVE
Update
What if I have to replace 1 with A, 2 with B, 3 with C and so on?
SELECT value,
COLUMN_VALUE AS words
FROM test_data t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT LISTAGG(
CHR( 64 + REGEXP_SUBSTR( t.value, '\d+', 1, LEVEL ) ),
','
) WITHIN GROUP ( ORDER BY LEVEL )
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.value, '\d+' )
) AS SYS.ODCIVARCHAR2LIST
)
) w;
Output:
VALUE WORDS
-------------------------- ----------------------------------------
9 I
6 F
1 A
2,3 B,C
5,6,7 E,F,G
8,4 H,D
1,2,3,4,5,6,7,8,9,10,11,12 A,B,C,D,E,F,G,H,I,J,K,L

Oracle 11g split text column to rows

I have table:
ID |Values
-----+--------------------------------
1 |AB,AD
2 |AG, ... ,BD
3 |AV
How can i transform it to:
ID |Value
-----+------
1 |AB
1 |AD
2 |AG
... |...
2 |BD
3 |AV
Using the built-in XML functions, you can do it like that:
with sample_data as
(
select 1 id, 'AB,AD' vals from dual union all
select 2, 'AG,AK,AJ,BA,BD' from dual union all
select 3, 'AV' from dual
)
select id, cast(t.column_value.extract('//text()') as varchar2(10)) val
from sample_data,
table( xmlsequence( xmltype(
'<x><x>' || replace(vals, ',', '</x><x>') || '</x></x>'
).extract('//x/*'))) t;
Result:
ID VAL
--- -----
1 AB
1 AD
2 AG
2 AK
2 AJ
2 BA
2 BD
3 AV
Using recursive common table expression, the same query looks like this:
with sample_data as
(
select 1 id, 'AB,AD' vals from dual union all
select 2, 'AG,AK,AJ,BA,BD' from dual union all
select 3, 'AV' from dual
),
split_first(id, val, rem) as
(
select id,
coalesce(substr(vals, 1, instr(vals, ',') - 1), vals) val,
case when instr(vals, ',') > 0 then substr(vals, instr(vals, ',') + 1) end rem
from sample_data
union all
select id,
coalesce(substr(rem, 1, instr(rem, ',') - 1), rem) val,
case when instr(rem, ',') > 0 then substr(rem, instr(rem, ',') + 1) end rem
from split_first
where rem is not null
)
select id, val from split_first
order by id;
Or a slightly different approach:
with sample_data as
(
select 1 id, 'AB,AD' vals from dual union all
select 2, 'AG,AK,AJ,BA,BD' from dual union all
select 3, 'AV' from dual
),
pos(id, seq, vals, sta, stp) as
(
select id, 1, vals, 1, instr(vals, ',') from sample_data
union all
select id, seq + 1, vals, stp + 1, instr(vals, ',', stp + 1) from pos
where stp > 0
)
select id, substr(vals, sta, case when stp > 0 then stp - sta else length(vals) end) from pos
order by id, seq;