Specific Pattern Matching in Oracle - oracle

I have a close to 1000 records which have first two characters as alphabets and then an number of characters.
eg.
- BE123
- QT12124
- ST1000
- XY12345
and similar data....
I have a table X in Oracle having a column 'Serial Number' which will be having similar data , but it is of standard length 7 and also start with first two characters as alphabets.
I want to do a pattern matching on the column Serial Number where i can use LIKE and '%' matching on the characters after first two characters in the column for eg
if the column has a data
- BE00123 , it should start give me BE123 as matched data
- QT12124 , it is matched data
- ST11001 , unmatched data
- XY12345, matched data

Well, this returns what you asked. See if it helps.
SQL> with t_one (col) as
2 (select 'BE123' from dual union all
3 select 'QT12124' from dual union all
4 select 'ST1000' from dual union all
5 select 'XY12345' from dual
6 ),
7 t_two (col) as
8 (select 'BE00123' from dual union all
9 select 'QT12124' from dual union all
10 select 'ST11001' from dual union all
11 select 'XY12345' from dual
12 )
13 select o.col
14 from t_one o join t_two t on substr(o.col, 1, 2) = substr(t.col, 1, 2)
15 and instr(t.col, substr(o.col, 3)) > 0;
COL
-------
BE123
QT12124
XY12345
SQL>
Line 14: match first two characters
Line 15: check whether characters from T_ONE table, starting from position 3, are contained in the T_TWO table's column value

Related

Getting data from a column only if it contains number or special characters in Oracle

I want data from a column only if it contains special characters or numbers.
My query looks as follows:
Select First_name from account where regexp_like(First_name,'[0-9]')
But I don't know how to achieve that special character thing
Well you're most of the way there already, just add the special characters you are interested in to the character class you've already defined in the regexp:
Select First_name
from account
where regexp_like(First_name,'[0-9##$_!?*]')
To also select records where First_name is null use one of the two following queries:
Select First_name
from account
where regexp_like(First_name,'[0-9##$_!?*]')
or First_name is null
or
Select First_name
from account
where regexp_like(nvl(First_name,'!'),'[0-9##$_!?*]')
The first one explicitly selects rows where First_name is null while the second query gets it by substituting a special character string for null strings.
How about upside-down approach? Select values that aren't all letters. How? Remove them from the string! Something like this:
SQL> with account (id, first_name) as
2 (select 1, 'Little' from dual union all -- valid
3 select 2, 'der Leyen' from dual union all -- valid
4 select 3, 'F00t' from dual union all -- invalid; two zeros
5 select 4, 'Sco#tt' from dual union all -- invalid; #
6 select 5, 'Me#SO' from dual union all -- invalid; #
7 select 6, 'What_is_it' from dual union all -- invalid; _
8 select 7, '12.345' from dual union all -- invalid; digits
9 select 8, 'Huh? Whoa!' from dual -- invalid; ?!
10 )
11 select id,
12 first_name,
13 regexp_replace(first_name, '[[:alpha:] ]', null) repl
14 from account
15 where regexp_replace(first_name, '[[:alpha:] ]', null) is not null
16 order by id;
ID FIRST_NAME REPL
---------- ---------- ----------
3 F00t 00
4 Sco#tt #
5 Me#SO #
6 What_is_it __
7 12.345 12.345
8 Huh? Whoa! ?!
6 rows selected.
SQL>
Column REPL is here to show what's left after replacing letters (and a space) with null; you wouldn't normally display it.

Regular Expression with Connect by

I have this query:
select regexp_substr('1,2,3,4,5','[^,]+',1,level)
from test1
connect by instr('1,2,3,4,5',',',1,level-1)>0;
Please help me to understand this query especially the use of a level and connect by and level-1.
LEVEL and CONNECT BY are used to generate sequences, see this simple example:
select level
from dual
connect by level < 10;
LEVEL
=======
1
2
3
4
5
6
7
8
9
instr('1,2,3,4,5', ',' , 1, level-1) > 0 counts the number of elements separated by comma maybe this version is easier to understand, it does the same:
select regexp_substr('1,2,3,4,5','[^,]+',1,level)
from dual
connect by LEVEL <= REGEXP_COUNT('1,2,3,4,5', ',')+1;
LEVEL <= REGEXP_COUNT('1,2,3,4,5', ',')+1 or instr('1,2,3,4,5', ',' , 1, level-1) > 0 means "run five times".
So, your select is basically a shortcut for
select regexp_substr('1,2,3,4,5','[^,]+', 1, 1) from dual union all
select regexp_substr('1,2,3,4,5','[^,]+', 1, 2) from dual union all
select regexp_substr('1,2,3,4,5','[^,]+', 1, 3) from dual union all
select regexp_substr('1,2,3,4,5','[^,]+', 1, 4) from dual union all
select regexp_substr('1,2,3,4,5','[^,]+', 1, 5) from dual;
#Wernfried's answer is excellent, but you should know there is a big risk with the regex of the format '[^,]+' which one sees often as an example of how to parse delimited strings.
This only works when all elements of the list are present. For an eye-opener, try it with the 2nd element as NULL:
select regexp_substr('1,,3,4,5', '[^,]+', 1, 3) from dual;
REGEXP_SUBSTR('1,,3,4,5','[^,]+',1,3)
-------------------------------------
4
1 row selected.
WHAT? '4' is most definitely NOT the 3rd element of that list! As you can see the NULL is not handled.
Please use this format of REGEXP_SUBSTR which does handle NULL list elements:
select regexp_substr('1,,3,4,5','(.*?)(,|$)', 1, 3, NULL, 1) from dual;
REGEXP_SUBSTR('1,,3,4,5','(.*?)(,|$)',1,3,NULL,1)
-------------------------------------------------
3
1 row selected.
The regex defines defines 2 groups, an optional set of any characters followed by a comma or the end of the line. The arguments to REGEXP_SUBSTR
say to return the 1st group of the 3rd instance of this match.

How to select second split of column data from oracle database

I want to select the data from a Oracle table, whereas the table columns contains the data as , [ex : key,value] separated values; so here I want to select the second split i.e, value
table column data as below :
column_data
++++++++++++++
asper,worse
tincher,good
golder
null -- null values need to eliminate while selection
www,ewe
from the above data, desired output like below:
column_data
+++++++++++++
worse
good
golder
ewe
Please help me with the query
According to data you provided, here are two options:
result1: regular expressions one (get the 2nd word if it exists; otherwise, get the 1st one)
result2: SUBSTR + INSTR combination
SQL> with test (col) as
2 (select 'asper,worse' from dual union all
3 select 'tincher,good' from dual union all
4 select 'golder' from dual union all
5 select null from dual union all
6 select 'www,ewe' from dual
7 )
8 select col,
9 nvl(regexp_substr(col, '\w+', 1, 2), regexp_substr(col, '\w+', 1,1 )) result1,
10 --
11 nvl(substr(col, instr(col, ',') + 1), col) result2
12 from test
13 where col is not null;
COL RESULT1 RESULT2
------------ -------------------- --------------------
asper,worse worse worse
tincher,good good good
golder golder golder
www,ewe ewe ewe
SQL>

Parse Values with Delimiters Using REGEXP_SUPSTR in Oracle 10g

I have a table called TVL_DETAIL that contains column TVL_CD_LIST. Column TVL_CD_LIST contains three records:
TVL_CD_LIST:
M1180_Z6827
K5900_Z6828
I2510
I've used the following code in an attempt to return the values only(so excluding the underscore):
SELECT
TVL_CD_LIST
FROM TVL_DETAIL
WHERE TVL_CD_LIST IN (SELECT regexp_substr(TVL_CD_LIST,'[^_]+', 1, level) FROM DUAL
CONNECT BY regexp_substr(TVL_CD_LIST,'[^_]+', 1, level) IS NOT NULL)
What I was expecting to see returned in separate rows was:
M1180
Z6827
K5900
Z6828
I2510
But it only returns I2510(which is the original value that doesn't contain an underscore).
What am I doing wrong? Any help is appreciated. Thanks!
To answer your question, you are querying for the list where it matches a sub-element and that will only happen where the list is comprised of one element. What you really wanted to select are the sub-elements themselves.
Note: Explanation of why parsing strings using the regex form '[^_]+' is bad here: https://stackoverflow.com/a/31464699/2543416
You want to parse the list, selecting the elements:
SQL> with TVL_DETAIL(TVL_CD_LIST) as (
select 'M1180_Z6827' from dual union
select 'K5900_Z6828' from dual union
select 'I2510' from dual
)
SELECT distinct regexp_substr(TVL_CD_LIST, '(.*?)(_|$)', 1, level, NULL, 1) element
FROM TVL_DETAIL
CONNECT BY level <= LENGTH(regexp_replace(TVL_CD_LIST, '[^_]', '')) + 1;
-- 11g CONNECT BY level <= regexp_count(TVL_CD_LIST, '_') + 1;
ELEMENT
-----------
Z6827
K5900
M1180
I2510
Z6828
SQL>
And this is cool if you want to track by row and element within row:
SQL> with TVL_DETAIL(row_nbr, TVL_CD_LIST) as (
select 1, 'M1180_Z6827' from dual union
select 2, 'K5900_Z6828' from dual union
select 3, 'I2510' from dual
)
SELECT row_nbr, column_value substring_nbr,
regexp_substr(TVL_CD_LIST, '(.*?)(_|$)', 1, column_value, NULL, 1) element
FROM TVL_DETAIL,
TABLE(
CAST(
MULTISET(SELECT LEVEL
FROM dual
CONNECT BY level <= LENGTH(regexp_replace(TVL_CD_LIST, '[^_]', '')) + 1
-- 11g CONNECT BY LEVEL <= REGEXP_COUNT(TVL_CD_LIST, '_')+1
) AS sys.OdciNumberList
)
)
order by row_nbr, substring_nbr;
ROW_NBR SUBSTRING_NBR ELEMENT
---------- ------------- -----------
1 1 M1180
1 2 Z6827
2 1 K5900
2 2 Z6828
3 1 I2510
SQL>
EDIT: Oops, edited to work with 10g as REGEXP_COUNT is not available until 11g.
The query you have used creates the list but you are comparing the list of record with column it self using the in clause, as such M1180 or Z6827 cannot be equal to M1180_Z6827 and so for K5900_Z6828. I2510 has only one value so it gets matched.
You can use below query if your requirement is exactly what you have mentioned in your desired output.
SQL> WITH tvl_detail AS
2 (SELECT 'M1180_Z6827' tvl_cd_list FROM dual
3 UNION ALL
4 SELECT 'K5900_Z6828' FROM dual
5 UNION ALL
6 SELECT 'I2510' FROM dual)
7 ---------------------------
8 --- End of data preparation
9 ---------------------------
10 SELECT regexp_substr(tvl_cd_list, '[^_]+', 1, LEVEL) AS tvl_cd_list
11 FROM tvl_detail
12 CONNECT BY regexp_substr(tvl_cd_list, '[^_]+', 1, LEVEL) IS NOT NULL
13 AND PRIOR tvl_cd_list = tvl_cd_list
14 AND PRIOR sys_guid() IS NOT NULL;
OUTPUT:
TVL_CD_LIST
--------------------------------------------
I2510
K5900
Z6828
M1180
Z6827

Getting Results in Horizontal way in oracle

I wrote query
select s_id from emp
where s_inv=12
i got results in this manner
1
2
3
4
5
but i want it in this format
1 2 3 4 5
If you need your result in a single column, you can use LISTAGG:
with emp(s_id, s_inv) as
(
select 1, 12 from dual union all
select 2, 12 from dual union all
select 3, 12 from dual union all
select 4, 12 from dual union all
select 5, 12 from dual
)
select listagg(s_id, ' ') within group (order by s_id)
from emp
where s_inv = 12
If you need to build many columns on the same row, you should first define how many columns will your result have

Resources