SQL: parse a column delimited (shorten the script) - sql

I created a query and it's properly working.. but I'm not yet satisfied because my code is too long, is there way that I can simplify or shorten my select statement ?
select
/*GenInfo*/
id ,name name,
replace(regexp_substr(properties, 'EntityID=[^;]*'), 'EntityID=', '') as EntityID,
replace(regexp_substr(properties, 'deployed=[^;]*'), 'deployed=', '') as deployed,
replace(regexp_substr(properties, 'type=[^;]*'), 'type=', '') as type,
replace(regexp_substr(properties, 'level=[^;]*'), 'level=', '') as "LEVEL",
replace(regexp_substr(properties, 'description=[^;]*'), 'description=', '') as description,
replace(regexp_substr(properties, 'indicator=[^;]*'), 'indicator=', '') as indicator,
replace(regexp_substr(properties, 'Agreement=[^;]*'), 'Agreement=', '') as Agreement,
replace(regexp_substr(properties, 'Activation date to charge=[^;]*'), 'Activation date to charge=', '') as Activationdatetocharge,
replace(regexp_substr(properties, 'id=[^;]*'), 'id=', '') as id,
replace(regexp_substr(properties, 'name=[^;]*'), 'name=', '') as name,
replace(regexp_substr(properties, 'currencyCode=[^;]*'), 'currencyCode=', '') as currencyCode,
replace(regexp_substr(properties, 'saleExpirationDate=[^;]*'), 'saleExpirationDate=', '') as saleExpirationDate,
replace(regexp_substr(properties, 'Product type=[^;]*'), 'Product type=', '') as Producttype,
replace(regexp_substr(properties, 'saleEffectiveDate=[^;]*'), 'saleEffectiveDate=', '') as saleEffectiveDate,
replace(regexp_substr(properties, 'Deactivation date to charge=[^;]*'), 'Deactivation date to charge=', '') as Deactivationdatetocharge
.
.
.
.
.
.
from OFFER
where name = 'PLAN 599'
;

You have to double-split and generate a query:
Split Row into "ColName=Value"-fields
Split fields into two seperate vars
Create a from-dual-query
DECLARE
inputstring VARCHAR2 (2000) := 'EntityID=1;deployed=2018-01-01;type=app';
myquery VARCHAR2 (2000) := 'SELECT'; -- result-var
tmpValue VARCHAR2 (2000);
tmpName VARCHAR2 (2000);
BEGIN
FOR i IN
(
SELECT TRIM (REGEXP_SUBSTR (inputstring, -- Split input and loop through
'[^;]+',
1,
LEVEL))
l
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT (inputstring, ';') + 1
)
LOOP
tmpName := REGEXP_SUBSTR (i.l, '[^=]+', 1, 1); -- Split column into value and name
tmpValue := REGEXP_SUBSTR (i.l, '[^=]+', 1, 2);
myquery := myquery || ' ''' || tmpValue || ''' as ' || tmpName || ','; -- build some query
END LOOP;
myQuery := SUBSTR (myQuery, 0, LENGTH (myQuery) - 1) || ' FROM DUAL'; -- complete query
DBMS_OUTPUT.put_line (myQuery); --output result
-- Result: SELECT '1' as EntityID, '2018-01-01' as deployed, 'app' as type FROM DUAL
END;
Anyway you'll get a problem with this query if you want to read out the values by code.
Perhaps you could tell us, what you want to do with the data.
I hope, you only want to convert some data from a file. If so you can add another split-loop to split rows.
Example-Function
CREATE OR REPLACE FUNCTION MakeSQL (inputstring VARCHAR2)
RETURN VARCHAR2
IS
myquery VARCHAR2 (2000); -- result-var
tmpValue VARCHAR2 (2000);
tmpName VARCHAR2 (2000);
BEGIN
FOR x IN ( SELECT TRIM (REGEXP_SUBSTR (inputstring, -- Split input and loop through
'[^' || CHR (10) || ']+',
1,
LEVEL))
tmpRow
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT (inputstring, CHR (10)) + 1)
LOOP
myquery := myquery || 'SELECT';
FOR i IN ( SELECT TRIM (REGEXP_SUBSTR (x.tmpRow, -- Split input and loop through
'[^;]+',
1,
LEVEL))
l
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT (x.tmpRow, ';') + 1)
LOOP
tmpName :=
REGEXP_SUBSTR (i.l,
'[^=]+',
1,
1); -- Split column into value and name
tmpValue :=
REGEXP_SUBSTR (i.l,
'[^=]+',
1,
2);
myquery :=
myquery || ' ''' || tmpValue || ''' as ' || tmpName || ','; -- build some query
END LOOP;
myQuery :=
SUBSTR (myQuery, 0, LENGTH (myQuery) - 1)
|| ' FROM DUAL UNION ALL'
|| CHR (10); -- complete row-select
END LOOP;
myQuery := SUBSTR (myQuery, 0, LENGTH (myQuery) - 11); -- complete query
DBMS_OUTPUT.put_line (myQuery); --output result
RETURN myQuery;
END MakeSQL;
Example call
SELECT MakeSQL('EntityID=1;deployed=2018-01-01;type=app
EntityID=2;deployed=2018-02-02;type=app') FROM DUAL;
Example-Result
SELECT '1' as EntityID, '2018-01-01' as deployed, 'app' as type FROM DUAL UNION ALL
SELECT '2' as EntityID, '2018-02-02' as deployed, 'app' as type FROM DUAL

Related

SQL Oracle object%rowtype dynamic

I have an object which is a ROWTYPE from a table and need to recover some of the columns to an array list.
For example:
The ROWTYPE contents Name1, Last_Name1, Id1, Adress1...Name25,Last_Name25,Id25,Adress25 in columns.
I need to know if I can recover them by a dynamic way with a loop to an array type like this:
Name(1-25),Last_Name(1-25),Id(1-25),Adress(1-25).
Given a simplified example of the problem with the table:
CREATE TABLE table_name (id1, name1, id2, name2, id3, name3, id4, name4) AS
SELECT 1, 'N1', 2, 'N2', 3, 'N3', 4, 'N4' FROM DUAL;
I am assuming, that to get the %ROWTYPE variable, you are using something like:
DECLARE
data TABLE_NAME%ROWTYPE;
BEGIN
SELECT * INTO data FROM table_name FETCH FIRST ROW ONLY;
DBMS_OUTPUT.PUT_LINE('1: ' || data.id1 || ', ' || data.name1);
DBMS_OUTPUT.PUT_LINE('2: ' || data.id2 || ', ' || data.name2);
DBMS_OUTPUT.PUT_LINE('3: ' || data.id3 || ', ' || data.name3);
DBMS_OUTPUT.PUT_LINE('4: ' || data.id4 || ', ' || data.name4);
END;
/
Rather than trying to dynamically access the %ROWTYPE record, why do you not bypass the problem and separate the values in the SELECT statement using UNPIVOT in a cursor and then you can use %ROWTYPE on the cursor (rather than the table):
DECLARE
CURSOR cur IS
SELECT id, name
FROM (SELECT * FROM table_name FETCH FIRST ROW ONLY)
UNPIVOT (
(id, name)
FOR idx IN (
(id1, name1) AS 1,
(id2, name2) AS 2,
(id3, name3) AS 3,
(id4, name4) AS 4
)
);
TYPE cur_row_arr IS TABLE OF cur%ROWTYPE;
rw cur%ROWTYPE;
arr cur_row_arr := cur_row_arr();
BEGIN
OPEN cur;
LOOP
FETCH cur INTO rw;
EXIT WHEN cur%NOTFOUND;
arr.EXTEND;
arr(arr.COUNT) := rw;
END LOOP;
CLOSE cur;
FOR i IN 1 .. arr.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( i || ': ' || arr(i).id || ', ' || arr(i).name );
END LOOP;
END;
/
db<>fiddle here
Don't try to do it dynamically, just hard-code the values.
Given the simplified example:
CREATE TABLE table_name (id1, name1, id2, name2, id3, name3, id4, name4) AS
SELECT 1, 'N1', 2, 'N2', 3, 'N3', 4, 'N4' FROM DUAL;
Then:
CREATE PROCEDURE test(
v_row IN TABLE_NAME%ROWTYPE
)
IS
TYPE details_type IS RECORD(
id TABLE_NAME.ID1%TYPE,
name TABLE_NAME.NAME1%TYPE
);
TYPE details_tab_type IS TABLE OF details_type;
details details_tab_type := details_tab_type();
BEGIN
details.EXTEND(4);
details(1) := details_type(v_row.id1, v_row.name1);
details(2) := details_type(v_row.id2, v_row.name2);
details(3) := details_type(v_row.id3, v_row.name3);
details(4) := details_type(v_row.id4, v_row.name4);
FOR i IN 1 .. details.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(i || ': ' || details(i).id || ', ' || details(i).name);
END LOOP;
END;
/
All you need to do is write the first assignment statement details(1) := details_type(v_row.id1, v_row.name1); and then copy-paste multiple copies and increment the numbers by 1 each time.
Then you can call it using:
DECLARE
v_row TABLE_NAME%ROWTYPE;
BEGIN
SELECT * INTO v_row FROM table_name FETCH FIRST ROW ONLY;
test(v_row);
END;
/
Which outputs:
1: 1, N1
2: 2, N2
3: 3, N3
4: 4, N4
db<>fiddle here

How to take where clause conditions from table column in oracle SQL or plsql

How to take where clause conditions from table column in oracle plsql.
E.g. data in table
Condition
1.sourceSystemId = 'SN'
2.AND(coverageType='AD',amountType1='PREMIUM',premiumFrequency='REGULAR',yearOfPremium='1')
e.g query:
select * from xyz where rule='abc' and "sourceSystemId = 'SN'"
select * from xyz where rule='abc' AND(coverageType='AD',amountType1='PREMIUM',premiumFrequency='REGULAR',yearOfPremium='1')
Not entirely sure what you're asking here, but I would imagine that
select * from xyz where rule='abc' AND(coverageType='AD',amountType1='PREMIUM',premiumFrequency='REGULAR',yearOfPremium='1')
would become
select * from xyz
where rule='abc'
AND coverageType='AD'
and amountType1='PREMIUM'
and premiumFrequency='REGULAR'
and yearOfPremium='1'
I suppose you want something like :
DECLARE
l_query VARCHAR2(2000) := 'select * from xyz where rule=''abc''';
l_result xyz%ROWTYPE;
l_cursor SYS_REFCURSOR;
BEGIN
dbms_output.put_line(l_query);
FOR clause IN (SELECT condition
FROM conditions)
LOOP
l_query := l_query||' AND '||clause.condition;
END LOOP;
OPEN l_cursor FOR l_query;
LOOP
FETCH l_cursor INTO l_result;
EXIT WHEN l_cursor%NOTFOUND;
..
-- your processing
END LOOP;
CLOSE l_cursor;
END;
Here is example of SQL solution. I used justt first and last condition but you can get them all...
WITH
xyz As
(
Select 1 "ID", 'abc' "RULE", 'AD' "COVERAGETYPE", 'PREMIUM' "AMOUNTTYPE1", 'REGULAR' "PREMIUMFREQUENCY", '1' "YEAROFPREMIUM" From Dual
UNION
Select 2 "ID", 'abc' "RULE", 'BF' "COVERAGETYPE", 'ORDINARY' "AMOUNTTYPE1", 'EXTRA' "PREMIUMFREQUENCY", '2' "YEAROFPREMIUM" From Dual
UNION
Select 3 "ID", 'abc' "RULE", 'AD' "COVERAGETYPE", 'PREMIUM' "AMOUNTTYPE1", 'REGULAR' "PREMIUMFREQUENCY", '1' "YEAROFPREMIUM" From Dual
),
conditions As
(
SELECT UPPER('coverageType=AD,amountType1=PREMIUM,premiumFrequency=REGULAR,yearOfPremium=1') "CND" From Dual
)
SELECT
x.ID, x.RULE, x.COVERAGETYPE, x.AMOUNTTYPE1, x.PREMIUMFREQUENCY, x.YEAROFPREMIUM
FROM
xyz x
INNER JOIN
conditions c ON(1=1)
WHERE
x.RULE = 'abc' And
x.COVERAGETYPE = CASE WHEN InStr(c.CND || ',', 'COVERAGETYPE=') = 0 THEN x.COVERAGETYPE
ELSE SubStr(SubStr(c.CND || ',', InStr(c.CND || ',', 'COVERAGETYPE=') + Length('COVERAGETYPE=')), 1, InStr(SubStr(c.CND || ',', InStr(c.CND || ',', 'COVERAGETYPE=') + Length('COVERAGETYPE=') + 1), ',')) END And
x.YEAROFPREMIUM = CASE WHEN InStr(c.CND || ',', 'YEAROFPREMIUM=') = 0 THEN x.YEAROFPREMIUM
ELSE SubStr(SubStr(c.CND || ',', InStr(c.CND || ',', 'YEAROFPREMIUM=') + Length('YEAROFPREMIUM=')), 1, InStr(SubStr(c.CND || ',', InStr(c.CND || ',', 'YEAROFPREMIUM=') + Length('YEAROFPREMIUM=') + 1), ',')) END
Result:
ID RULE COVERAGETYPE AMOUNTTYPE1 PREMIUMFREQUENCY YEAROFPREMIUM
1 abc AD PREMIUM REGULAR 1
3 abc AD PREMIUM REGULAR 1

Dynamically generate sql statement based on metadata about the data being queried

I would like to dynamically query data that is staged as a long string by defining how to read the string and how to split it up.
So I can define the data with the following elements
FIELD_NAME VARCHAR2(30) NOT NULL,
DATA_TYPE VARCHAR2(20) NOT NULL,
COLUMN_ID NUMBER NOT NULL,
FIELD_START_POS NUMBER,
FIELD_END_POS NUMBER,
FIELD_LEN NUMBER,
ROW_TYPE VARCHAR2(10),
DATE_MASK VARCHAR2(12)
sample data in this table
can I take that info to create a select that would look something like
SELECT CASE cd.data_type
WHEN 'DATE'
THEN
TO_DATE (SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len), cd.date_mask)
WHEN 'NUMBER'
THEN
TO_NUMBER (SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len))
ELSE
TRIM (SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len))
END
AS cd.field_name
FROM staged_data sd, column_definitions cd
I am having difficulties trying to tie the 2 together.
I know I could pivot the column names in the definition out like so:
SELECT *
FROM column_definitions
PIVOT (max(field_name) FOR column_id IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20))
but this still results in many rows
My goal is to generate this statement so that is can be run via the EXECUTE IMMEDIATE so it could work for many different files just by defining how to read the string.
I also have the need to read different row types hence the row_type column which will be defined for the same file but had their own column order and columns.
So I have been able to generate a string that is the select I am looking for based on the metadata about the staged file like this:
DECLARE
select_items VARCHAR2 (4000);
BEGIN
FOR c IN ( SELECT *
FROM column_definitions
WHERE file_pk = 1 AND row_type = 1
ORDER BY column_id)
LOOP
IF c.data_type = 'NUMBER'
THEN
select_items :=
select_items
|| 'CASE WHEN is_number(SUBSTR(row_data,'
|| c.field_start_pos
|| ','
|| c.field_len
|| ')) = ''TRUE'' THEN TO_NUMBER(SUBSTR(row_data,'
|| c.field_start_pos
|| ','
|| c.field_len
|| ')) ELSE NULL END AS '
|| c.field_name
|| ',';
ELSIF c.data_type = 'DATE'
THEN
select_items :=
select_items
|| 'CASE WHEN ISDATE(SUBSTR(row_data,'
|| c.field_start_pos
|| ','
|| c.field_len
|| '))=''true'' THEN TO_DATE(SUBSTR(row_data,'
|| c.field_start_pos
|| ','
|| c.field_len
|| '),'''
|| c.date_mask
|| ''') ELSE NULL END AS '
|| c.field_name
|| ',';
ELSE
select_items :=
select_items
|| 'TRIM(SUBSTR(row_data,'
|| c.field_start_pos
|| ','
|| c.field_len
|| ')) AS '
|| c.field_name
|| ',';
END IF;
END LOOP;
select_items := SUBSTR (select_items, 1, LENGTH (select_items) - 1);
select_items :=
'SELECT '
|| select_items
|| ' FROM STAGED_FILE where row_type=1 AND rownum <= 1000;';
DBMS_OUTPUT.PUT_LINE (select_items);
END;
this spits out something like this:
SELECT CASE
WHEN is_number (SUBSTR (row_data, 1, 1)) = 'TRUE'
THEN
TO_NUMBER (SUBSTR (row_data, 1, 1))
ELSE
NULL
END
AS REC_TYPE_IND,
SUBSTR (row_data, 11, 4) AS SRVC_LOC,
CASE
WHEN ISDATE (SUBSTR (row_data, 15, 8)) = 'true'
THEN
TO_DATE (SUBSTR (row_data, 15, 8), 'YYYYMMDD')
ELSE
NULL
END
AS BEGIN_DT,
CASE
WHEN ISDATE (SUBSTR (row_data, 23, 8)) = 'true'
THEN
TO_DATE (SUBSTR (row_data, 23, 8), 'YYYYMMDD')
ELSE
NULL
END
AS END_DT,
SUBSTR (row_data, 31, 50) AS ID,
SUBSTR (row_data, 101, 2) AS COUNTY_CD,
SUBSTR (row_data, 103, 30) AS ADDR_LN_1,
SUBSTR (row_data, 133, 30) AS ADDR_LN_2,
SUBSTR (row_data, 163, 18) AS CITY,
SUBSTR (row_data, 181, 2) AS STATE_CD,
CASE
WHEN is_number (SUBSTR (row_data, 183, 5)) = 'TRUE'
THEN
TO_NUMBER (SUBSTR (row_data, 183, 5))
ELSE
NULL
END
AS ZIP_CD,
CASE
WHEN is_number (SUBSTR (row_data, 188, 4)) = 'TRUE'
THEN
TO_NUMBER (SUBSTR (row_data, 188, 4))
ELSE
NULL
END
AS ZIP_CD4,
CASE
WHEN is_number (SUBSTR (row_data, 192, 10)) = 'TRUE'
THEN
TO_NUMBER (SUBSTR (row_data, 192, 10))
ELSE
NULL
END
AS PHONE_NUM
FROM staged_FILE
WHERE row_type = 1 AND ROWNUM <= 1000;
Now off to solve how to dynamically create an associative array to stuff the data into or another way to work with the data.
In your example, you use a CASE statement. Your first expression has a DATE datatype, the second has NUMBER and the third is a VARCHAR2. From the documentation:
For a simple CASE expression, the expr and all comparison_expr values
must either have the same datatype or must all have a
numeric datatype.
Basically, you can't do this because there's no way to know at compile time what the datatype of the field_name column is.
This is not a straightforward problem to solve, since you don't know what your datatype is going to be until runtime. Even once you get a dynamic SQL statement, what sort of variable are you going to select the data into?
I think you're basically going to have to:
Using column_definitions, construct a string that contains a SQL statement appropriate for the data type in question.
Create a TYPE that contains members of all the possible resulting data types.
Use either EXECUTE IMMEDIATE or DBMS_SQL to parse and execute that string, then fetch the result into an instance of that type.
You may actually be best off not doing this via SQL at all. Instead, I would probably do the following:
Get the data type of interest from column_definitions.
Use SUBSTR to extract the region of interest from the string in staged_data.
Do something like:
.
l_token := SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len);
IF l_datatype = 'DATE' THEN
l_date := TO_DATE( l_token, 'yyyy-mm-dd' );
ELSIF l_datatype = 'NUMBER' THEN
l_number := TO_NUMBER( l_token);
....
END IF;
I would not expect high performance from this sort of approach.

Capitalize words in a string

I've used INITCAP to capitalize words in a string, but I've run into a small issue:
select initcap(q'[JOE'S CARD 'N' CANDY]') from dual;
It returns "Joe'S Card 'N' Candy", but I wonder if there is another way to capitalize the words so it will look like this "Joe's Card 'N' Candy" (notice the s is in lowercase)
In your place I would create a custom PL/SQL procedure of the kind:
create or replace function initcap_cust(p_input varchar2)
return varchar2
as
l_input varchar2(4000) := lower(p_input);
l_capitalize_first_letter boolean := true;
l_output varchar2(4000) := null;
l_curr_char char(1);
begin
-- here we iterate over the lowercased string characters
for i in 1..length(l_input) loop
l_curr_char := substr(l_input, i, 1);
-- if we find a space - OK, next alphabet letter should be capitalized
-- you can add here more delimiters, e.g.: l_curr_char in (' ', ',', etc)
if l_curr_char = ' ' then
l_capitalize_first_letter := true;
end if;
-- makes O'Sullivan look this way
if regexp_like(l_output, '(^| )O''$') then
l_capitalize_first_letter := true;
end if;
-- found the first letter after delimiter - OK, capitalize
if l_capitalize_first_letter and (l_curr_char between 'a' and 'z') then
l_curr_char := upper(l_curr_char);
l_capitalize_first_letter := false;
end if;
-- build the output string
l_output := l_output || l_curr_char;
end loop;
return l_output;
end;
It works in your case and similar ones. Also it can be customized depending on your needs without dealing with complex queries built using the only functions provided by Oracle out of the box.
N.B. Also there is an option to create equivalent java stored procedure, on the link provided by Edgars T. there is an example.
Adapted from this answer to use a single simpler regular expression to parse each word:
WITH names ( name ) AS (
SELECT 'FIRSTNAME O''MALLEY' FROM DUAL UNION
SELECT 'FIRST''NAME TEH''TE' FROM DUAL UNION
SELECT 'FORMAT ME BYGGER''N' FROM DUAL UNION
SELECT 'OLD MCDONALD' FROM DUAL UNION
SELECT 'EVEN OL''DER MACDONALD' FROM DUAL UNION
SELECT q'[JOE'S CARD 'N' CANDY]' FROM DUAL
)
SELECT name,
formatted_name
FROM names
MODEL
PARTITION BY (ROWNUM rn)
DIMENSION BY (0 dim)
MEASURES(name, CAST('' AS VARCHAR2(255)) word, CAST('' AS VARCHAR(255)) formatted_name)
RULES ITERATE(99) UNTIL (word[0] IS NULL)
(
word[0] = REGEXP_SUBSTR(name[0], '[^ ]+( *|$)', 1, ITERATION_NUMBER + 1),
formatted_name[0] = formatted_name[0]
-- Capitalise names starting with ', *', MC and MAC:
|| INITCAP(REGEXP_SUBSTR( word[0], '^([^'']?''|ma?c)?(.)(.*)$', 1, 1, 'i', 1 ) )
-- Capitalise the next letter of the word
|| UPPER( REGEXP_SUBSTR( word[0], '^([^'']?''|ma?c)?(.)(.*)$', 1, 1, 'i', 2 ) )
-- Lower case the rest of the word
|| LOWER( REGEXP_SUBSTR( word[0], '^([^'']?''|ma?c)?(.)(.*)$', 1, 1, 'i', 3 ) )
);
Output:
NAME FORMATTED_NAME
----------------------- ----------------------
EVEN OL'DER MACDONALD Even Ol'der MacDonald
OLD MCDONALD Old McDonald
FIRST'NAME TEH'TE First'name Teh'te
FORMAT ME BYGGER'N Format Me Bygger'n
JOE'S CARD 'N' CANDY Joe's Card 'N' Candy
FIRSTNAME O'MALLEY Firstname O'Malley

Union results from subselect

I have an SQL statement with following structure:
f(string parameter)=select * from table(parameter) ...
=> results in some table with rows.
f(string parameter) is used as shortcut for more complex sql subquery.
table parameters:
|---params----|
param1
param2
param3
....
how to combine f() with the table parameters where f() will be called for each param in this table. My question refers to SQL statement itself. As result I expect
f(param1)
union all
f(param2)
union all
f(param3)
...
If someone is interested what is staying in f() refer to my previous question answer
https://stackoverflow.com/a/27599637/2023524.
You can define f as a function like the below
create or replace function f(param IN VARCHAR2) return varchar2 is
Result varchar2(32767);
begin
with names as(
SELECT REGEXP_SUBSTR(regexp_replace(replace(param,
chr(13) || chr(10),
','),
'[[:space:]]*',
''),
'[^=]+',
1,
level) as name
FROM DUAL
CONNECT BY LEVEL <= (SELECT REGEXP_COUNT(param, '=') FROM DUAL)
)
select LISTAGG(nvl(regexp_substr(name, '[^,]+', 1, 2),
regexp_substr(name, '[^,]+', 1)),
',') WITHIN
GROUP(
ORDER BY name)
INTO Result
from names;
return(Result);
end f;
Then you can call your parameters like the below
with parameter(param) as (
select 'aa = black' ||chr(13)||chr(10)||'bb = change'||chr(13)||chr(10)||'mmmm=no'
from dual union all
select 'aa = black' ||chr(13)||chr(10)||'bb = change'||chr(13)||chr(10)||'kkk=no' from dual
)
select distinct regexp_substr(f(param),'[^,]+',1,level) from parameter
connect by level <=regexp_count(f(param),',')+1;
Update1:-Just for your information You can call a function within an Anonymous Block like the below
DECLARE
function f(param IN VARCHAR2) return varchar2 is
Result varchar2(32767);
begin
with names as(
SELECT REGEXP_SUBSTR(regexp_replace(replace(param,
chr(13) || chr(10),
','),
'[[:space:]]*',
''),
'[^=]+',
1,
level) as name
FROM DUAL
CONNECT BY LEVEL <= (SELECT REGEXP_COUNT(param, '=') FROM DUAL)
)
select LISTAGG(nvl(regexp_substr(name, '[^,]+', 1, 2),
regexp_substr(name, '[^,]+', 1)),
',') WITHIN
GROUP(
ORDER BY name)
INTO Result
from names;
return(Result);
end f;
begin
for i in 1 .. (regexp_count(f('aa = black' || chr(13) || chr(10) ||
'bb = change' || chr(13) || chr(10) ||
'kkk=no'),
',') + 1) loop
dbms_output.put_line(regexp_substr(f('aa = black' || chr(13) ||
chr(10) || 'bb = change' ||
chr(13) || chr(10) || 'kkk=no'),
'[^,]+',
1,
i));
end loop;
end;
In Oracle 11g, I think you can just do this in the from clause:
with params as (
select param1 as param from dual union all
select param2 union all
select param3
)
select *
from params p,
table(f(p.param)) f;