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;
Related
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
How to rearrange the letter in string in alphabetical order in SQL
For example
cbaz to abcz
You can split the string up into characters and then aggregate:
WITH characters ( rid, value, ch, i, l ) AS (
SELECT ROWID,
value,
SUBSTR(value, 1, 1),
1,
LENGTH(value)
FROM table_name
UNION ALL
SELECT rid,
value,
SUBSTR(value, i + 1, 1),
i + 1,
l
FROM characters
WHERE i < l
)
SELECT MAX( value ) AS original,
LISTAGG(ch) WITHIN GROUP ( ORDER BY ch ) AS ordered
FROM characters
GROUP BY rid
or:
SELECT value As original,
ordered
FROM table_name t
CROSS APPLY (
SELECT LISTAGG(SUBSTR(t.value, LEVEL, 1))
WITHIN GROUP (ORDER BY SUBSTR(t.value, LEVEL, 1)) AS ordered
FROM DUAL
CONNECT BY LEVEL <= LENGTH(t.value)
)
Which, for the sample data:
CREATE TABLE table_name ( value ) AS
SELECT 'cbaz' FROM DUAL UNION ALL
SELECT 'zyx' FROM DUAL UNION ALL
SELECT 'zyx' FROM DUAL;
Outputs:
ORIGINAL
ORDERED
cbaz
abcz
zyx
xyz
zyx
xyz
db<>fiddle here
Just for fun, you could do this programmatically:
with function sort_letters
( p_str varchar2 )
return varchar2
as
type charList is table of simple_integer index by varchar2(1);
letters charList;
letter varchar2(1);
sorted_letters long;
begin
if p_str is not null then
for i in 1..length(p_str) loop
letter := substr(p_str,i,1);
letters(letter) :=
case
when letters.exists(letter) then letters(letter) +1
else 1
end;
end loop;
letter := letters.first;
loop
sorted_letters := sorted_letters || rpad(letter, letters(letter), letter);
letter := letters.next(letter);
exit when letter is null;
end loop;
end if;
return sorted_letters;
end;
select sort_letters('abracadabra')
from dual
/
SORT_LETTERS('ABRACADABRA')
---------------------------
aaaaabbcdrr
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
I need to split the record for column CMD.NUM_MAI which may contain ',' or ';'.
I did this but it gave me an error:
SELECT REGEXP_SUBSTR (expression.num_mai,
'[^;|,]+',
1,
LEVEL)
FROM (SELECT CMD.num_cmd,
(SELECT COMM.com
FROM COMM
WHERE COMM.cod_soc = CMD.cod_soc AND COMM.cod_com = 'URL_DSD')
AS cod_url,
NVL (CONTACT.nom_cta, TIERS.nom_ct1) AS nom_cta,
NVL (CONTACT.num_mai, TIERS.num_mai) AS num_mai,
NVL (CONTACT.num_tel, TIERS.num_tel) AS num_tel,
TO_CHAR (SYSDATE, 'hh24:MI') AS heur_today
FROM CMD, TIERS, CONTACT
WHERE ( (CMD.cod_soc = :CMD_cod_soc)
AND (CMD.cod_eta = :CMD.cod_eta)
AND (CMD.typ_cmd = :CMD.typ_cmd)
AND (CMD.num_cmd = :CMD.num_cmd))
AND (TIERS.cod_soc(+) = CMD.cod_soc)
AND (TIERS.cod_trs(+) = CMD.cod_trs_tra)
AND (TIERS.cod_soc = CONTACT.cod_soc(+))
AND (TIERS.cod_trs = CONTACT.cod_trs(+))
AND (CONTACT.lib_cta(+) = 'EDITION')) experssion
CONNECT BY REGEXP_SUBSTR (expression.num_mai,'[^;|,]+',1,LEVEL)
Error 1:
The expression in CONNECT BY clause is unary. You have to specify both left and right hand side operands.
Try something like,
CONNECT BY REGEXP_SUBSTR (expression.num_mai,'[^;|,]+',1,LEVEL) IS NOT NULL
Error 2:
Your bind variable name is wrong. Ex: :CMD_cod_eta
Perhaps you wanted this way!
( (CMD.cod_soc = :CMD_cod_soc)
AND (CMD.cod_eta = :CMD_cod_eta)
AND (CMD.typ_cmd = :CMD_typ_cmd)
AND (CMD.num_cmd = :CMD_num_cmd))
This is a common question, I'd put into a function, then call it as needed:
CREATE OR REPLACE function fn_split(i_string in varchar2, i_delimiter in varchar2 default ',', b_dedup_tokens in number default 0)
return sys.dbms_debug_vc2coll
as
l_tab sys.dbms_debug_vc2coll;
begin
select regexp_substr(i_string,'[^' || i_delimiter || ']+', 1, level)
bulk collect into l_tab
from dual
connect by regexp_substr(i_string, '[^' || i_delimiter || ']+', 1, level) is not null
order by level;
if (b_dedup_tokens > 0) then
return l_tab multiset union distinct l_tab;
end if;
return l_tab;
end;
/
This will return a table of varchar2(1000), dbms_debug_vc2coll, which is a preloaded type owned by SYS (or you could create your own type using 4000 perhaps). Anyway, an example using it (with space, comma, or semi-colon used as delimiters):
with test_data as (
select 1 as id, 'A;test;test;string' as test_string from dual
union
select 2 as id, 'Another string' as test_string from dual
union
select 3 as id,'A,CSV,string' as test_string from dual
)
select d.*, column_value as token
from test_data d, table(fn_split(test_string, ' ,;', 0));
Output:
ID TEST_STRING TOKEN
1 A;test;test;string A
1 A;test;test;string test
1 A;test;test;string test
1 A;test;test;string string
2 Another string Another
2 Another string string
3 A,CSV,string A
3 A,CSV,string CSV
3 A,CSV,string string
You can pass 1 instead of 0 to fn_split to dedup the tokens (like the repeated "test" token above)
I want a query that returns the number of sequential match of words in two strings
example:
Table
Id column1 column2 result
1 'foo bar live' 'foo bar' 2
2 'foo live tele' 'foo tele' 1
3 'bar foo live' 'foo bar live' 0
to get total number of occurrence I am using:
select id, column1,column2,
extractvalue(dbms_xmlgen.getxmltype('select cardinality (
sys.dbms_debug_vc2coll(''' || replace(lower(column1), ' ', ''',''' ) || ''') multiset intersect
sys.dbms_debug_vc2coll('''||replace(lower(column2), ' ', ''',''' )||''')) x from dual'), '//text()') cnt
from table.
Can anyone please suggest a query on similar lines for sequential match also as I want number of sequential matches and number of occurrences shown together.
Personally, in this situation, I would choose PL/SQL code over plain SQL. Something like:
Package specification:
create or replace package PKG is
function NumOfSeqWords(
p_str1 in varchar2,
p_str2 in varchar2
) return number;
end;
Package body:
create or replace package body PKG is
function NumOfSeqWords(
p_str1 in varchar2,
p_str2 in varchar2
) return number
is
l_str1 varchar2(4000) := p_str1;
l_str2 varchar2(4000) := p_str2;
l_res number default 0;
l_del_pos1 number;
l_del_pos2 number;
l_word1 varchar2(1000);
l_word2 varchar2(1000);
begin
loop
l_del_pos1 := instr(l_str1, ' ');
l_del_pos2 := instr(l_str2, ' ');
case l_del_pos1
when 0
then l_word1 := l_str1;
l_str1 := '';
else l_word1 := substr(l_str1, 1, l_del_pos1 - 1);
end case;
case l_del_pos2
when 0
then l_word2 := l_str2;
l_str2 := '';
else l_word2 := substr(l_str2, 1, l_del_pos2 - 1);
end case;
exit when (l_word1 <> l_word2) or
((l_word1 is null) or (l_word2 is null));
l_res := l_res + 1;
l_str1 := substr(l_str1, l_del_pos1 + 1);
l_str2 := substr(l_str2, l_del_pos2 + 1);
end loop;
return l_res;
end;
end;
Test case:
with t1(Id1, col1, col2) as(
select 1, 'foo bar live' ,'foo bar' from dual union all
select 2, 'foo live tele' ,'foo tele' from dual union all
select 3, 'bar foo live' ,'foo bar live'from dual
)
select id1
, col1
, col2
, pkg.NumOfSeqWords(col1, col2) as res
from t1
;
Result:
ID1 COL1 COL2 RES
---------- ------------- ------------ ----------
1 foo bar live foo bar 2
2 foo live tele foo tele 1
3 bar foo live foo bar live 0
Why to give up on the query approach. I know it's a bit complicated and I hope someone can work on it to improve it, but working on this during my spare time I was able to survive a an afternoon of calls...
Here on SQLFidlle
SELECT Table1.id,
Table1.column1,
Table1.column2,
max(nvl(t.l,0)) RESULT
FROM (
SELECT id,
column1,
column2,
LEVEL l,
decode(LEVEL,
1,
substr(column1, 1, instr(column1,' ', 1, LEVEL) -1),
substr(column1, 1, (instr(column1,' ', 1, LEVEL )))
) sub1,
decode(LEVEL,
1,
substr(column2, 1, instr(column2,' ', 1, LEVEL) -1),
substr(column2, 1, (instr(column2,' ', 1, LEVEL )))
) sub2
FROM (SELECT id,
column1 || ' ' column1,
column2 || ' ' column2
FROM Table1)
WHERE decode(LEVEL,
1,
substr(column1, 1, instr(column1,' ', 1, LEVEL) -1),
substr(column1, 1, (instr(column1,' ', 1, LEVEL )))
) =
decode(LEVEL,
1,
substr(column2, 1, instr(column2,' ', 1, LEVEL) -1),
substr(column2, 1, (instr(column2,' ', 1, LEVEL )))
)
START WITH column1 IS NOT NULL
CONNECT BY instr(column1,' ', 1, LEVEL) > 0
) t
RIGHT OUTER JOIN Table1 ON trim(t.column1) = Table1.column1
AND trim(t.column2) = Table1.column2
AND t.id = Table1.id
GROUP BY Table1.id,
Table1.column1,
Table1.column2
ORDER BY max(nvl(t.l,0)) DESC
I know this issue is old but I found a good solution:
You can test from here https://rextester.com/l/oracle_online_compiler
select
id1,
col1,
col2,
(
Select Count(*)
From
(Select Upper(To_Char(Trim(Substr(Column_Value,0,Length(Column_Value))))) w1
From xmltable(('"' || Replace(Replace(col1,' ', ','), ',', '","') || '"'))
Where Upper(To_Char(Trim(Substr(Column_Value,0,Length(Column_Value))))) Is Not Null) c1,
(Select Upper(To_Char(Trim(Substr(Column_Value,0,Length(Column_Value))))) w2
From xmltable(('"' || Replace(Replace(col2,' ', ','), ',', '","') || '"'))
Where Upper(To_Char(Trim(Substr(Column_Value,0,Length(Column_Value))))) Is Not Null) c2
Where c1.w1 = c2.w2
) Test
From
(select 1 Id1, 'foo bar live' col1, 'foo bar' col2 from dual union all
select 2, 'foo live tele pepe gato coche' ,'bar foo live tele perro gato' from dual union all
select 3, 'bar foo live tele perro gato' ,'foo bar live'from dual) t1