REGEXP_SUBSTR Remove All Chars after the Match - oracle

I am trying to extract Work Weeks from user comments, user comments might be like
"Something WW15 Target Complete"
"WW12.3 Good to Go"
Currently my script is grabbing the match but also everything after it,
Result = WW12.3 Good to Go
Ideal Result would be just the WW12.3 part.
I need to do a course and watch demos on REGEXP but thats for another day unfortunately.
SELECT
BATCH, OPERATOR, COMMENTS, TXN_DATE
,REGEXP_SUBSTR(COMMENTS,'WW[^;]+') as ETA
FROM F_LOTCOMMENTHIST
I have also tried doing this to remove everything after a space but it doesnt work:
SELECT
BATCH, OPERATOR, COMMENTS, TXN_DATE
,REPLACE(REGEXP_SUBSTR(COMMENTS,'WW[^;]+'),' [^;]+','') as ETA
FROM F_LOTCOMMENTHIST
I understand that I am probably using the wrong pattern string but I just cant figure out what I need when looking at example tables like:
https://www.techonthenet.com/oracle/functions/regexp_substr.php

I suggest using an online tester of regular expressions to learn using them.
For example this one: https://regexr.com/5p0m6
You can type your expression and text and you'll see directly the behaviour. Take a look at the cheatsheet and explain section.

Would this do? Oracle, though, I don't know db2.
SQL> with test (col) as
2 (select 'Something WW15 Target Complete' from dual union all
3 select 'WW12.3 Good to Go' from dual union all
4 select 'What is that WW1.2' from dual
5 )
6 select col,
7 regexp_substr(col, 'WW(\w+)?((\.)?(\d+))') result
8 from test;
COL RESULT
------------------------------ ----------
Something WW15 Target Complete WW15
WW12.3 Good to Go WW12.3
What is that WW1.2 WW1.2
SQL>

If you want to get WW[digits][dot][digits] not surrounded by any characters except space (may be), you may use negative lookahead & lookbehind (lookarounds) like below.
with test (col) as
(
values
'SomethingWW15.3 WW12 Target Complete'
, 'WW12.3 Good to Go'
, 'WW12.3What is that WW1.2'
)
select
col
, regexp_substr(col, '(?<![^ ])WW[\d]+\.?[\d]?(?![^ ])') result
from test;
|COL |RESULT|
|------------------------------------|------|
|SomethingWW15.3 WW12 Target Complete|WW12 |
|WW12.3 Good to Go |WW12.3|
|WW12.3What is that WW1.2 |WW1.2 |

Related

Passing multiple values through a Presentation Variable in a Direct Database Request

I have created multiple Direct Database Requests in OBIEE, I am able to pass a presentation variable to a Dashboard Prompt. However I am only able to select one value, if I select multiple values, then I get an error saying No results found. I have researched this on the Oracle community but have yet to find a solution, all similar questions have remained unanswered that I found.
When I select multiples values I notice that they are being passed as one long string. eg.
GL_Period IN ('APR-19,AUG-19,FEB-19,JAN-19,JUL-19,JUN-19,MAR-19,MAY-19,NOV-19,OCT-19,SEP-19')
There are beginning and ending quotes instead of quotes around each value.
My Question is can anyone help me get this prompt to accept multiple values. I have tried to update the SQL behind the dashboard prompt, my thinking was that if I concatenate ' to the beginning and end of each value. Then TRIM the first and last ' off that this would work. However I have been unable to get the syntax correct for this, if it is even possible.
Here is the code for my Presentation Variable in my Direct Database Request
AND (
gl_period_name IN ('#{P_Period}')
OR 'All' IN ('#{P_Period}{All}'))
I am able to use the following code to add ' ' to each value - This is for the Presentation Variable P_Period
SELECT CHAR (39)||"Time"."Fiscal Period"||CHAR (39) FROM "Financials - AP Transactions")
I tried the below query to combine the two but to no avail
TO_CHAR(TRIM(BOTH ''' FROM SELECT CHAR (39)||"Time"."Fiscal Period"||CHAR (39) FROM "Financials - AP Transactions"))
Use gl_period_name IN (#{P_Period}['#']{''}) to pass multiple values.
Only problem is this will give you no results if no value is passed (I haven't figured out how to solve that issue yet).
You can turn the comma separated string into multiple values with a Recursive CTE
WITH CTE AS (SELECT 'APR-19,AUG-19,FEB-19,JAN-19,JUL-19,JUN-19,MAR-19,MAY-19,NOV-19,OCT-19,SEP-19' TEMP FROM DUAL)
SELECT TRIM(REGEXP_SUBSTR( TEMP, '[^,]+', 1, LEVEL)) Period FROM CTE
CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(temp, '[^,]+')) + 1
Replacing the hard-coded string with your variable, put that into a sub-query in your where clause and you'll be good to go. Something like this:
select 1 TEST
from dual
where 'APR-19' IN (WITH CTE AS (SELECT 'APR-19,AUG-19,FEB-19,JAN-19,JUL-19,JUN-19,MAR-19,MAY-19,NOV-19,OCT-19,SEP-19' TEMP FROM DUAL)
SELECT TRIM(REGEXP_SUBSTR( TEMP, '[^,]+', 1, LEVEL)) Period FROM CTE
CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(temp, '[^,]+')) + 1)

Most efficient way to parse a Comma Separated list in ApEx

I am still working on my first solo Oracle ApEx(Application Express) application, so I am sure that this will be old hat for some of you. I tried to look up what I want to do, but I am not sure what to even look up. If there is already a thread that answers this, then I apologize for duplication, but I have searched here for about two hours trying to figure this out.
I am open minded to a solution since I have not already built anything for this part of the application yet, so I am not locked into one set way. If there is a better way, please let me know.
I want to obtain a comma separated (or semi-colon, or colon separated) list from the user. I then want to take that data and write it to a table with each value in its own row.
Example of input:
X12345678, X22345678, X32345678 (and so on)
The numbers that are input will then be looked up on a different table because we use non-identifying PIDM numbers (Anyone that has used Ellucian's Banner will understand). This select statement is crazy simple to retrieve this number:
Select spriden_pidm
from spriden
where spriden_change_ind is null
and spriden_id = :P5_STU_ID
Then, it will be stored in a table thusly:
Example of data storage:
ID | Semester | Creating User | Created Date | Data Origin
012345678 | 201640 | JDOE1 | sysdate | ApEx : 130
022345678 | 201640 | JDOE1 | sysdate | ApEx : 130
And so forth.
Question 1: I am presuming that a loop will be the best way to accomplish this using regular expressions. Would that be a correct presumption?
Question 2: Does ApEx already have something built in that would process this better and/or faster?
ApEx version 5.0, Oracle 12c
APEX_UTIL.string_to_table
and use the comma for the second parameter
As mentioned, since we were working against the clock on a deployment, we ended up writing a loop similar to what the APEX_UTIL.string_to_table (Thanks Rob van Wijk) accomplishes:
declare
v_id varchar2(4000) := :P5_NEW_IDS;
begin
for i in 1..regexp_count( v_id, ',' ) + 1 loop
insert into zresadddrop.zsrintl(zsrintl_pidm,
zsrintl_term_code_eff,
zsrintl_created_by,
zsrintl_created_date,
zsrintl_data_origin)
select distinct spriden_pidm
, :P5_Term_Code
, :app_user
, sysdate
, 'ApEx: '||:app_id
from spriden
where spriden_change_ind is null
and spriden_id = trim(zgeneral.get_token(v_id,i));
end loop;
commit;
end;

Strange behaviour, results from linked DB

Running this query on Toad
select * from customer#linkedDb
where "CustomerNumber"=1
Gets a single row results, however
select * from customer#linkedDb
where "CustomerNumber"=(select 1 from dual)
Still returns a single row, but almost every column is NULL
Any data-conversion functions or links to other tables cause this same effect. Any ideas?
Trying this on SQL+, I get this:
select "Forename" from customer#linkedDb where "CustomerNumber"=1
Result: Amy
select "Forename" from customer#linkedDb where "CustomerNumber"=(select 1 from dual)
Result: A m y
I am sure this must be some character encoding thing, but I'm stumped.
The problem is converting from nvarchar to varchar...
Select
Cast("Forename" As Varchar(50))
From
customer#linkedDb
Where
"CustomerNumber"=(select 1 from dual)
Doesn't work, because the (select 1 from dual) seems to cause the data to be autocast. What does work is any alteration to the value after casting - which I think may be prompting Oracle to change the order of things
Select
Substr(Cast("Forename" As Varchar(50)),1,50)
From
customer#linkedDb
Where
"CustomerNumber"=(select 1 from dual)
This works. It is an ugly - slightly expensive solution, so I have some workarounds in place - but I thought I would post this as food for thought if anyone else comes across the same problem.

easier way to do multiple substr instr OR statements oracle pl sql

I want to check every 2 positions of a table column for many different values. (selecting any row that matches any specified value in any of the 2 digit positions)
This is the only way I know of to do what I need, but I bet there is a cleaner, shorter way:
select *
from table
where 1=1
and (
Substr(columnA,1,2) IN ('CE','44','45','87','89','UT','AZ','XX','YY','S1','S2','S3','S4','ES','PM')
or Substr(columnA,3,2) IN ('CE','44','45','87','89','UT','AZ','XX','YY','S1','S2','S3','S4','ES','PM')
or Substr(columnA,5,2) IN ('CE','44','45','87','89','UT','AZ','XX','YY','S1','S2','S3','S4','ES','PM')
or Substr(columnA,7,2) IN ('CE','44','45','87','89','UT','AZ','XX','YY','S1','S2','S3','S4','ES','PM')
or Substr(columnA,9,2) IN ('CE','44','45','87','89','UT','AZ','XX','YY','S1','S2','S3','S4','ES','PM')
or Substr(columnA,11,2) IN ('CE','44','45','87','89','UT','AZ','XX','YY','S1','S2','S3','S4','ES','PM')
or Substr(columnA,12,2) IN ('CE','44','45','87','89','UT','AZ','XX','YY','S1','S2','S3','S4','ES','PM')
or Substr(columnA,14,2) IN ('CE','44','45','87','89','UT','AZ','XX','YY','S1','S2','S3','S4','ES','PM')
or Substr(columnA,16,2) IN ('CE','44','45','87','89','UT','AZ','XX','YY','S1','S2','S3','S4','ES','PM') )
;
note: if the column's value is ABCDEF, and we are checking for 'BC', this should not find a match, only 'AB', 'CD', 'EF' should match.
I want to be able to list everything I am searching for just once. Even better would be to only list columnA once.
I did find an INSTR function that may be useful, but I'm not sure how to apply it here.
this works:
Instr(columnA,'XX') IN (1,3,5,7,9,11,14,16)
But is there a better way than to do this for every value I am searching for?
Could I use COALESCE some how?
Using REGEXP_LIKE to match a regular expression? Something like that:
'^(..)*((CE)|(44)|(45)|(87)|(89)|(UT)|(AZ)|(XX)|(YY)|(S1)|(S2)|(S3)|(S4)|(ES)|(PM))'
^ ensure the regex is anchored to the start of line
(..)* eats 0 to many pair of characters
((CE)|...) matches one of your digraphs.
See http://sqlfiddle.com/#!4/d41d8/38453/0 for a live example.
This answer assumes that you intend to search every 2 characters. I am assuming the even numbered starting numbers are incorrect: technically you are searching overlapping strings in the original question.
Since this is a regular pattern , you can generate it, which can help you simplify the query.
WITH search_pattern AS
(SELECT LEVEL * 2 - 1 search_start
FROM DUAL
CONNECT BY LEVEL <= 9)
SELECT DISTINCT t.*
FROM table1 t CROSS JOIN search_pattern sp
WHERE SUBSTR (t.columna, search_start, 2) IN
('CE','44','45','87','89','UT','AZ','XX','YY','S1','S2','S3','S4','ES','PM')
In this case, we have one row for each position, which is functionally equivalent to or. The distinct keyword is necessary to prevent rows that qualify more than once from being returned more than once.
While this solution is functional, #SylvainLeroux's answer using regex will likely perform better.

Another client to replace sqlplus for access to oracle?

I have some issues with how sqlplus output is formatted for the terminal and I am just thinking of writing a script around sqlplus and fixing these.
On the other hand, wow that seems really lame. Because Oracle has several tons of tools written. Yet it seems difficult to get what I want. Does anyone have another suggestion?
First, I want smarter column widths. If I create a table with a column whose max size is 200 characters but then I put "abc", "xyz" and "123" in it, do I need a 200-space wide column on the terminal to display the contents? I do not think so. I think I need 3 characters plus a couple for padding. Yet Oracle insists on giving me a 200-character wide column. Unless there is somewhere to fix this.
Second, I want easy access to a sideways display of the columns, like using \G at the end of the command in MySQL. I know there is a way to do this in Oracle but it seemed complicated. Why could there not just be a simple switch? Like a \G at the end of the command? There can be if I wrap the output to sqlplus and do this myself.
So, the question seems to be this. Should I write a simple script around sqlplus to give me what I want, or is there a way to get Oracle to give me this behavior In sqlplus? And if there is, how much extra information will I have to stuff into my head to make this work? Because it does not seem as though it should be very complicated. But Oracle is certainly not making it easy.
First of all I suggest you look over the SQL*plus reference - you might find some useful tips there like adjusting a column width
COL column_name for a20
you can set up your own settings in the GLOGIN file. over time, like any other CMD, you'll get your preferences just right.
To describe a table you can use DESC. if you want more data write your own script and reuse it with #.
If all this doesn't work for you, you can always switch to a GUI like Toad or SQL developer.
EDIT:
I'm adding one of my own scripts to show you some tricks on how to make SQL*Plus more friendly on the command line. This one is for getting segment sizes.
/* This is the trick - clears &1 and &2 if received an empty string */
set ver off feed off
col 1 new_v 1
col 2 new_v 2
select 1,2 from dual where 1=0;
variable p_owner varchar2(30)
variable p_segment varchar2(30)
/* set bind variables */
begin
:p_owner := '&1';
:p_segment := '&2';
end;
/
set feed 1
break on segment_type skip 1
column MB for a25
select
segment_type,
decode(gi_segment_name + gi_segment_type + gi_tablespace_name , 3 ,'...Grand Total', segment_name) SEGMENT_NAME,
to_char(round(MB,3),'99,999,999.99') MB ,
nvl(tablespace_name,'-*-') tablespace_name
from (
select tablespace_name , segment_type , segment_name , sum(bytes/1024/1024) MB ,
grouping_id(segment_name) gi_segment_name ,
grouping_id(segment_type) gi_segment_type ,
grouping_id(segment_type) gi_tablespace_name
from dba_segments
where ((:p_owner is null and owner = user) or owner like upper(:p_owner))
and (:p_segment is null or segment_name like upper('%'||:p_segment||'%'))
group by rollup(tablespace_name, segment_type , segment_name)
)
where not (gi_segment_name = 1 and gi_segment_type = 0 and gi_tablespace_name = 0)
order by decode(segment_type,'TABLE','0','TABLE PARTITION','1','INDEX','2','INDEX PARTITION','3',segment_type) ,
(case when segment_name like '.%' then 'z' else 'a' end) ,
gi_segment_name ,
MB desc ,
segment_name;
clear break
/* clear definition for &1 and &2 after being used.
allows the variable to be null the next run. */
undefine 1
undefine 2
I'll walk you through some of the things iv'e done here
The script accepts two parameters. The first 4 lines clears the
parameter if none received. if you don't do this SQL*Plus will prompt
you for them. And we dont want that.
Setting the binds was more of a big deal in past version. It's
intended to save Hard / Soft parse. latest version solve this
problem. It's still a best practice though.
The break is a nice touch. You'll see it.
The grouping Id show me the sub totals on several levels.
I've added two parameter, owner and segment name. both can contain
wild card. both can be null. If non provided the query will fetch the
current user segments.
Order by decode enabled me to set a custom sort order for different
segment types. You can change it as you wish.
I'm executing the script like this
my segments :
#seg
Scott's segments
#seg scott
Scott's Emp related segments
#seg scott emp
I have similar scripts for session, longops, wait events, tables, constraints, locks, kill session etc .... during my daily routine i rarely write SQL for querying this stuff any more.

Resources