Substring using Oracle When there is multiple Matches? - oracle

Hi I am trying to fetch substring in oracle for the below two strings. I want result if the pattern matches starting with S9C and the having 8 numbers after S9C
For Eg: for the below two inputs I need output like
Input:
1.CABLE : THERMINAL 3X2X0.25MM FPCP PLUS UNITRONIC S9C000019651
2.Motor Protection Relay EMR-3MPO-2S9CB1-1 (was IQ1000II / MP 3000)
3.GREASE : BEM 41-132 3KG CARTRIDGE KLUBERPLEX S9C00019171 (Order by KG's required)
4.DO NOT USE CARRIER SPIDEX ZK 38 98 SH. S9C00011593 (SUPERSEDE BY S9C10204555 - WIN0020775)
Output:
1.Null
2.Null
3.S9C00019171
4.S9C10204555

You can use a combination of REGEXP_SUBSTR and REGEXP_COUNT to solve your question:
WITH test_data(input) AS
(
SELECT '1.CABLE : THERMINAL 3X2X0.25MM FPCP PLUS UNITRONIC S9C000019651' FROM DUAL UNION ALL
SELECT '2.Motor Protection Relay EMR-3MPO-2S9CB1-1 (was IQ1000II / MP 3000)' FROM DUAL UNION ALL
SELECT '3.GREASE : BEM 41-132 3KG CARTRIDGE KLUBERPLEX S9C00019171 (Order by KG''s required)' FROM DUAL UNION ALL
SELECT '4.DO NOT USE CARRIER SPIDEX ZK 38 98 SH. S9C00011593 (SUPERSEDE BY S9C10204555 - WIN0020775)' FROM DUAL
)
SELECT regexp_substr(td.input,
'S9C[0-9]{8}( |$)',
1,
GREATEST(1, regexp_count(td.input, 'S9C[0-9]{8}( |$)')))
FROM test_data td
Here's a quick explanation of how it works:
REGEXP_COUNT counts the occurrences of the target pattern, so we can get the last occurrence later.
Wrap the result in in GREATEST because 0 is invalid for REGEXP_SUBSTR parameter.
Call REGEXP_SUBSTR to grab the last occurrence of the target string.
Here is a DBFiddle to show you it working (DBFiddle)

Related

Substring of a string using Oracle (SUBSTRING or REGEXP_SUBSTR) When there is multiple Matches

Hi I am trying to fetch substring in oracle for the below two strings. I want result if the pattern matches starting with S9C and the having next numbers
For Eg: for the below two inputs I need output like
Input:
1.CABLE : THERMINAL 3X2X0.25MM FPCP PLUS UNITRONIC S9C000019651
2.Motor Protection Relay EMR-3MPO-2S9CB1-1 (was IQ1000II / MP 3000)
3.GREASE : BEM 41-132 3KG CARTRIDGE KLUBERPLEX S9C00019171 (Order by KG's required)
4.DO NOT USE CARRIER SPIDEX ZK 38 98 SH. S9C00011593 (SUPERSEDE BY S9C10204555 - WIN0020775)
Output:
1.S9C000019651
2.Null
3.S9C00019171
4.S9C10204555
Or Else the Better way is to fetch first matching String from back of the text.
I think you can achieve the desired result with below script where you can pick the second group. If that is null, Pick the first group -
WITH DATA AS (SELECT 'CABLE : THERMINAL 3X2X0.25MM FPCP PLUS UNITRONIC S9C000019651' STR FROM DUAL
UNION ALL
SELECT 'Motor Protection Relay EMR-3MPO-2S9CB1-1 (was IQ1000II / MP 3000)' FROM DUAL
UNION ALL
SELECT 'GREASE : BEM 41-132 3KG CARTRIDGE KLUBERPLEX S9C00019171 (Order by KG''s required)' FROM DUAL
UNION ALL
SELECT 'DO NOT USE CARRIER SPIDEX ZK 38 98 SH. S9C00011593 (SUPERSEDE BY S9C10204555 - WIN0020775)' FROM DUAL)
SELECT NVL(REGEXP_SUBSTR(STR, 'S9C\d{7,}', 1, 2), REGEXP_SUBSTR(STR, 'S9C\d{7,}'))
FROM DATA;
Demo.

How to get minimum unused number from a column in Oracle and Linq?

I have a column named voucher_number. The data in this column looks like
1, 2
I want a query (in oracle and linq as well) to return 0,3,4,5,6,7,8,9,10
Note: i am taking range (0 to 10 )as a parameter parameter from screen(aspx page)
You can use MINUS operator as following:
Select voucher_num from
(
(Select level - 1 as voucher_num from dual
Connect by level <= 11)
Minus
(Select voucher_number from your_table)
)
Order by voucher_num;
Cheers!!

How to use different group separators for thousands and millions. Oracle

I need to display different results in the next format, example:
40000000 to 40'000,000
I tried using this, but when i try 2 differents group separators i get the "invalid number format model" error:
select to_char(9999999999, '9g999g99999g9', 'NLS_NUMERIC_CHARACTERS='',.''')
from dual;
Also tried using substr and replace but it doesnt work in all the cases (like when the result is 3000000 or 700000000).
This works but it is not the optimal solution.:
SELECT substr(replace('40,000,000',',',''''),0,length(40000000)-2)|| substr('40,000,000',-4) from dual;
What the actual select look like if i use the previous code.
SELECT substr(replace(to_char(oTOTAL_SENIOR, '999,999,999'),',',''''),0,length(oTOTAL_SENIOR)-2)|| substr(to_char(oTOTAL_SENIOR, '999,999,999'),-4) from dual
The previous select gets bugged when i use substr replace and to_char together because of the '999,999,999'.
I also tried using regexp_replace but im not good at it.
I know i need to replace everything but the last 4 characters (,000) but i dont know how.
Any help will be aprreciated.
You'll need someone smarter than me to do that properly, but - meanwhile, see whether this helps.
I'm on XE 11g; in order to avoid "no more data to read from socket" error I got during the final steps of this query (I believe it was due to two string reversings), I created my own "reverse" function. There's undocumented reverse function, but I don't want to use it.
Basically, it reverses a string you pass as a parameter. What do I need it for? I found it simpler to reverse values, split them to 3-by-3-by-3 characters and apply those "strange" separators you want. Also, it makes the whole code simpler. It can be done without it, but - as I said - no more data to read from socket won't allow it. Sorry about that.
Now, someone will say: why didn't you (meaning: me, LF) do that using PL/SQL completely and put everything into a function? No particular reason & no problem in doing it, if necessary.
OK, here it is:
SQL> create or replace function f_reverse (par_string in varchar2)
2 return varchar2
3 is
4 retval varchar2(20);
5 begin
6 select listagg(substr(par_string, level, 1))
7 within group (order by level desc)
8 into retval
9 from dual
10 connect by level <= length(par_string);
11
12 return retval;
13 end;
14 /
Function created.
SQL> select f_reverse('1234') from dual;
F_REVERSE('1234')
---------------------------------------------------------------------
4321
SQL>
Finally, this is what you want:
SQL> with test (id, col) as
2 (select 1, 40100200 from dual union all
3 select 2, 2300400 from dual union all
4 select 3, 700500 from dual union all
5 select 4, 25700 from dual union all
6 select 5, 6300 from dual union all
7 select 6, 555 from dual
8 )
9 select id,
10 regexp_replace(
11 f_reverse(
12 substr(f_reverse(col), 1, 3) ||','||
13 substr(f_reverse(col), 4, 3) || chr(39) ||
14 substr(f_reverse(col), 7)
15 ), '^[^0-9]+', '') result
16 from test;
ID RESULT
---------- ----------
1 40'100,200
2 2'300,400
3 700,500
4 25,700
5 6,300
6 555
6 rows selected.
SQL>
What does it do?
lines #1 - 7 - sample data
lines #9 onward is useful code
lines #12 - 14 - splitting reversed sample data into substrings 3 characters in length
line #12 - concatenated , as this is thousands separator
line #13 - concatenaded chr(13) which is ', a millions separator
line #11 - reversing concatenated "reversed" string back
line #10 - removing possible non-numeric characters from the beginning of the result (those are separators for values that are shorter than "thousand" or "million")

FInd if the fifth position is a letter and not a number using ORACLE

How can I find if the fifth position is a letter and thus not a number using Oracle ?
My last try was using the following statement:
REGEXP_LIKE (table_column, '([abcdefghijklmnopqrstuvxyz])');
Perhaps you'd rather check whether 5th position contains a number (which means that it is not something else), i.e. do the opposite of what you're doing now.
Why? Because a "letter" isn't only ASCII; have a look at the 4th row in my example - it contains Croatian characters and these aren't between [a-z] (nor [A-Z]).
SQL> with test (col) as
2 (select 'abc_3def' from dual union all
3 select 'A435D887' from dual union all
4 select '!#$%&/()' from dual union all
5 select 'ASDĐŠŽĆČ' from dual
6 )
7 select col,
8 case when regexp_like(substr(col, 5, 1), '\d+') then 'number'
9 else 'not a number'
10 end result
11 from test;
COL RESULT
------------- ------------
abc_3def number
A435D887 not a number
!#$%&/() not a number
ASDĐŠŽĆČ not a number
SQL>
Anchor to the start of the string else you may get unexpected results. This works, but remove the caret (start of string anchor) and it returns 'TRUE'! Note it uses the case-insensitive flag of 'i'.
select 'TRUE'
from dual
where regexp_like('abcd4fg', '^.{4}[A-Z]', 'i');
Yet another way to do it:
regexp_like(table_column, '^....[[:alpha:]]')
Using the character class [[:alpha:]] will pick up all letters upper case, lower case, accented and etc. but will ignore numbers, punctuation and white space characters.
If what you care about is that the character is not a number, then use
not regexp_like(table_column, '^....[[:digit:]]')
or
not regexp_like(table_column, '^....\d')
Try:
REGEXP_LIKE (table_column, '^....[a-z]')
Or:
SUBSTR (table_column, 5, 1 ) BETWEEN 'a' AND 'z'

Replacing/Converting 1 to A with Oracle/PLSQL

Firstly, I greatly appreciate any feedback that anyone can offer. I am using Oracle SQL Developer, Version 4.0.2.15, Build 15.21.
I know and understand that many, many similar questions have been asked, as I've searched around on stackoverflow as well as the rest of the internet. However, the corresponding answers are either too vague or too extravagant, and attempt to do things that are way over my head and not what I am trying to accomplish. I am extremely new to SQL and haven't seriously done any coding since I did Java about 12 years ago. So please understand that something simple to you, is not so simple and obvious to me.
My bare-bones endstate that I am shooting for is taking a pre-existing Oracle Table Column, which is called 'service_level', that has parameters of 1-3, and making them A-C (where A=1, B=2, C=3). The reason for this is that I have an ArcGIS gdB featureclass that has a corresponding column, called 'MaintServi', with the parameters of A-C. I am going to join them using ArcToolbox once I have converted/replaced the 1-3 to A-C, and have exported them from Oracle into an Arc gdB as another table. The reason being is that the featureclass (obviously) has geometry, but this particular Oracle table does not.
From what I have gathered I know (or think) I will need to use something like:
chr(ord('a') + 3)
^ Where I will need to use/call upon the chr/ord functions. However, due to my inexperience, I cannot think of how to properly call this without getting an error. Below is what I have for my query thus far (but without chr/ord). I just need to figure out how to correctly insert it into my query to achieve the desired results.
SELECT v_wv_wp_crew.*,
Substr(v_wv_wp_crew.winter_supp_id, 1, 6) AS CostCenter,
Substr(v_wv_wp_crew.winter_supp_id, 8, 11) AS Crew_Supp_ID
FROM v_wv_wp_crew
WHERE crew_on_road >= '13-FEB-12'
AND ( operation = 2
OR operation = 3 );
Thanks again and hopefully I have complied with the posting rules of stackoverflow.
# Mark J. Bobak -
When implementing his ideas I get either this (Like I said, i'm not sure how to insert it properly without receiving an error)
SELECT v_wv_wp_crew.*,
Substr(v_wv_wp_crew.winter_supp_id, 1, 6) AS CostCenter,
Substr(v_wv_wp_crew.winter_supp_id, 8, 11) AS Crew_Supp_ID
FROM v_wv_wp_crew
WHERE crew_on_road >= '13-FEB-12'
AND ( operation = 2
OR operation = 3 )
UNION ALL
WITH service_level as (select 1 service_level from dual
union all
select 2 service_level from dual union all
select 3 service_level from dual)
select decode(service_level,1,'A',2,'B',3,'C') from service_level;
I receive the following error:
*ORA-32034: unsupported use of WITH clause
32034. 00000 - "unsupported use of WITH clause"
*Cause: Inproper use of WITH clause because one of the following two reasons
1. nesting of WITH clause within WITH clause not supported yet
2. For a set query, WITH clause can't be specified for a branch.
3. WITH clause can't sepecified within parentheses.
Action: correct query and retry
Error at Line: 14 Column: 25
Or I receive an output of only 3 rows (A, B, C) if I run the query separately - sorry I don't have enough reputation to post the image yet.
You can use the DECODE() function. Something like this should work:
with list_of_digits as (select 1 col_a from dual
union all
select 2 col_a from dual
union all
select 3 col_a from dual
union all
select 4 col_a from dual)
select decode(col_a,1,'A',2,'B',3,'C','Other') from list_of_digits;
Using your query, try this:
WITH service_level as (select 1 service_level from dual
union all
select 2 service_level from dual union all
select 3 service_level from dual)
select decode(service_level,1,'A',2,'B',3,'C') from service_level
union all
SELECT v_wv_wp_crew.*,
Substr(v_wv_wp_crew.winter_supp_id, 1, 6) AS CostCenter,
Substr(v_wv_wp_crew.winter_supp_id, 8, 11) AS Crew_Supp_ID
FROM v_wv_wp_crew
WHERE crew_on_road >= '13-FEB-12'
AND ( operation = 2
OR operation = 3 );
ord isn't an Oracle function. The equivalent Oracle function is ASCII. However, even substituting in the correct function, I don't see how that gets you what you want.
It seems most likely that you just want to add a column (I'd use case to translate the values):
SELECT v_wv_wp_crew.*,
Substr(v_wv_wp_crew.winter_supp_id, 1, 6) AS CostCenter,
Substr(v_wv_wp_crew.winter_supp_id, 8, 11) AS Crew_Supp_ID,
case service_level
when '1' then 'a'
when '2' then 'b'
when '3' then 'c'
end as service_level_alpha
FROM v_wv_wp_crew
WHERE crew_on_road >= '13-FEB-12'
AND ( operation = 2
OR operation = 3 );
If you want to return this column as service_level, then you'll need to return the full list of columns instead of using the asterisk.
Since this is a straight-forward character swap, you could use translate to really streamline the operation: translate(service_level,'123','abc'). However, I vastly prefer case over either decode or translate for readability

Resources