Use REGEXP_SUBSTR in DB2 - db2

I want to use something like REGEXP_SUBSTR in DB2 (version 10.5).
There is an example of what I tried:
SELECT REGEXP_SUBSTR('hello to you', '.o')
FROM sysibm.sysdummy1
I got this error : [Error Code: -420, SQL State: 22018]
09:23:12 [SELECT - 0 row(s), 0.000 secs] [Error Code: -420, SQL State: 22018] DB2 SQL Error: SQLCODE=-420, SQLSTATE=22018, SQLERRMC=INTEGER, DRIVER=3.57.82
... 1 statement(s) executed, 0 row(s) affected, exec/fetch time: 0.000/0.000 sec [0 successful, 0 warnings, 1 errors]

There is no equivalent function to REGEXP_SUBSTR in DB2.
However you can achieve similar results with the XMLQUERY function
SELECT
XMLCAST(
XMLQUERY('fn:replace($src,"^hello | you$","")'
PASSING 'hello to you' AS "src")
AS VARCHAR(255))
FROM SYSIBM.SYSDUMMY1;
Difficulty Here, fn:replace removes matched patterns because DB2's implementation doesn't support () and $1 sub-grouping patterns

With DB2 V11.1 there is now REGEXP_SUBSTR(). It works simply as this:
Example 1
SELECT REGEXP_SUBSTR('hello to you', '.o',1,1)
FROM sysibm.sysdummy1Copy
Return the string which matches any character preceding a 'o'.
The result is 'lo'.
Example 2
SELECT REGEXP_SUBSTR('hello to you', '.o',1,2)
FROM sysibm.sysdummy1Copy
Return the second string occurrence which matches any character preceding a 'o'.
The result is 'to'.
Example 3
SELECT REGEXP_SUBSTR('hello to you', '.o',1,3)
FROM sysibm.sysdummy1
Return the third string occurrence which matches any character preceding a 'o'.
The result is 'yo'.

Related

Converting function instr from Oracle to PostgreSQL (sql only)

I am working on converting something from Oracle to PostgreSQL. In the Oracle file there is a function:
instr(string,substring,starting point,nth location)
Example:
instr(text, '$', 1, 3)
In PostgreSQL this does not exist, so I looked up an equivalent function (4 parameter is important).
I found:
The function strpos(str, sub) in Postgres is equivalent of instr(str, sub) in Oracle. Tried options via split_part (it didn't work out).
I need the same result only with standard functions Postgres (not own function).
Maybe someone will offer options, even redundant in code.
This may be done in pure SQL using string_to_array.
with tab(val) as (
select 'qwe$rty$123$456$78'
union all
select 'qwe$rty$123$'
union all
select '123$456$'
union all
select '123$456'
)
select
val
/*Oracle's signature: instr(string , substring [, position [, occurrence ] ])*/
, case
when
array_length(
string_to_array(substr(val /*string*/, 1 /*position*/), '$' /*substring*/),
1
) <= 3 /*occurrence*/
then 0
else
length(array_to_string((
string_to_array(substr(val /*string*/, 1 /*position*/), '$' /*substring*/)
)[:3/*occurrence*/],
'$'/*substring*/)
) + 1
end as instr
from tab
val
instr
qwe$rty$123$456$78
12
qwe$rty$123$
12
123$456$
0
123$456
0
Postgres: fiddle
Oracle: fiddle

Database-migration ms sql to postgresql

I am getting a syntax error when converting between SQL Server to PostgreSQL. Any thoughts?
IF (var_port_with_bmrk_total_mv != 0 AND var_bmrk_info IS NOT NULL) THEN
BEGIN
insert into t$tmp_diff
select #asof_dt asof_dt,#choiceID choiceID ,p.input_array_type ,p.group_order, CONVERT(DECIMAL(32,10),p.port_value/#var_port_total_mv) port_value,convert(decimal(32,10), isnull(bmrk_value/#port_with_bmrk_total_mv,0)) bmrk_value
from t$tmp_port_sum p, t$tmp_bmrk_sum b
where p.input_array_type=b.input_array_type and p.group_order = b.group_order
END;
ELSE
Original before conversion
insert into #tmp_other_diff
select #asof_dt asof_dt,#choiceID choiceID , b.input_array_type,b.grouping,convert(decimal(32,10),0) port_value, (bmrk_value/#port_with_bmrk_total_mv) bmrk_value
from #tmp_bmrk_other_sum b
where b.key_value not in ( select p.key_value from #tmp_port_other_sum p)
Error message:
Error occurred during SQL query execution
Reason:
SQL Error [42601]: ERROR: syntax error at or near ","
Position: 9030
the relevant comma being:
CONVERT(DECIMAL(32,10),p.port_value
There is no convert() function in Postgres. Use the SQL standard cast or the Postgres extension ::data type. In this case:
...., cast(0 as decimal(30,10)) port_value, ....
OR
...., 0::decimal(30,10) port_value, ...
Note: No comma after the expression. In the original port_value is the column alias. You need to keep it that way.

It can't calculate count(*) in a query onto DB2 database

I want to do a count(*) of the number of a rows fom a DB2 database.
The basic query is the following:
select
SUBSTR("Request_Detail",LOCATE('/',"Request_Detail")+1,LOCATE('/',"Request_Detail",LOCATE('/',"Request_Detail")+1)-LOCATE('/',"Request_Detail"))
from "Request_Analisys"
WHERE
"Sample_Date_and_Time">=1200323230000000 and "Sample_Date_and_Time"<1200332300000000
and "Request_Detail" <> '[Summary]'
and "Request_Detail" not like 'WS:%'
Now I'd like to do a count(*) of the resulting rows, but if I do a query like this:
select
count(*),
SUBSTR("Request_Detail",LOCATE('/',"Request_Detail")+1,LOCATE('/',"Request_Detail",LOCATE('/',"Request_Detail")+1)-LOCATE('/',"Request_Detail"))
from "Request_Analisys"
WHERE
"Sample_Date_and_Time">=1200323230000000 and "Sample_Date_and_Time"<1200332300000000
and "Request_Detail" <> '[Summary]'
and "Request_Detail" not like 'WS:%'
It gives the error:
18:51:58 FAILED [SELECT - 0 rows, 0.032 secs] 1) [Code: -119, SQL State: 42803] An expression starting with "Request_Detail" specified in a SELECT clause, HAVING clause, or ORDER BY clause is not specified in the GROUP BY clause or it is in a SELECT clause, HAVING clause, or ORDER BY clause with a column function and no GROUP BY clause is specified.. SQLCODE=-119, SQLSTATE=42803, DRIVER=4.22.29
2) [Code: -727, SQL State: 56098] An error occurred during implicit system action type "2". Information returned for the error includes SQLCODE "-119", SQLSTATE "42803" and message tokens "Request_Detail".. SQLCODE=-727, SQLSTATE=56098, DRIVER=4.22.29
How could I do to get the count of the rows?
Which Request_Detail line's substr would you think it shows after the count?
If you count the lines, the result set will be a single line, and using any columns in it makes no sense.
If you want multiple lines, with a count for each found substr, you need to GROUP BY this substr.
This may work...
select
count(
SUBSTR("Request_Detail"
,LOCATE('/',"Request_Detail")+1
,LOCATE('/',"Request_Detail",LOCATE('/',"Request_Detail")+1)
-LOCATE('/',"Request_Detail")))
)
from "Request_Analisys"
WHERE
"Sample_Date_and_Time">=1200323230000000 and "Sample_Date_and_Time"<1200332300000000
and "Request_Detail" <> '[Summary]'
and "Request_Detail" not like 'WS:%'
But if not this should..
with cte as (
select
SUBSTR("Request_Detail"
,LOCATE('/',"Request_Detail")+1
,LOCATE('/',"Request_Detail",LOCATE('/',"Request_Detail")+1)
-LOCATE('/',"Request_Detail"))) as mydetail
from "Request_Analisys"
WHERE
"Sample_Date_and_Time">=1200323230000000 and "Sample_Date_and_Time"<1200332300000000
and "Request_Detail" <> '[Summary]'
and "Request_Detail" not like 'WS:%'
)
select count(*) from cte
I suggest you use REGEXP_EXTRACT to pick what you want out of your "Request_Detail" column. This is more flexable than using SUBSTR and LOCATE, and will avoid the statement was not executed because a numeric argument of a scalar function is out of range.. error
e.g
select
REGEXP_EXTRACT("Request_Detail",'.*/(.+/)',1,1,'',1)
, SUBSTR("Request_Detail",LOCATE('/',"Request_Detail")+1,LOCATE('/',"Request_Detail",LOCATE('/',"Request_Detail")+1)-LOCATE('/',"Request_Detail"))
FROM TABLE(VALUES('aaaa/bbbb/ccc')) AS T("Request_Detail")
returns
1 |2
------|-----
bbbb/ |bbbb/
so, you could then do this
SELECT
COUNT(*)
, REGEXP_EXTRACT("Request_Detail",'.*/(.+/)',1,1,'',1)
FROM
"Request_Analisys"
GROUP BY
REGEXP_EXTRACT("Request_Detail",'.*/(.+/)',1,1,'',1)
for example

Oracle hierarchy data representation ambiguity

I am having a table in following structure.
_________________________________
|| ExpObjkey Exp1 Exp2 operator||
________________________________
1 2 3 +
2 4 5 +
3 6 7 -
I want to have records in the following order:
for expObjKey=1, we will have
ExpObjKey Expression
1 (4+5)+(6-7)
explanation:
for ExpObjKey 1 we will have 2 +3
then 2 will have 4+5
and 3 will have 6+7.
Its more like a hierarchy.
I have tried lots of possible ways but no near the solution.
SELECT expObjkey, SYS_CONNECT_BY_PATH(exp1||' ' ||operator|| exp2||')', ' ( ') "Path"
FROM bpmn_expression
CONNECT BY PRIOR
exp1=expObjkey or exp2=expObjkey
start with expObjkey=1
I don't think you can do this with a hierarchical query, but I'd be interested to be proven wrong. You'd need to start at the bottom of the tree and work your way up to allow the substitutions to happen; but then there doesn't seem to be a way to combine the partial expressions that produces. It might be possible with recursive subquery factoring, but that isn't available until 11gR2.
On 10g you could use your own recursive function to generate what you need:
create or replace function get_expression(p_key bpmn_expression.expobjkey%type)
return varchar2 is
row bpmn_expression%rowtype;
begin
select * into row from bpmn_expression where expobjkey = p_key;
return '(' || get_expression(row.exp1)
|| row.operator || get_expression(row.exp2) || ')';
exception
when no_data_found then
return to_char(p_key);
end;
/
select get_expression(1) as expression from dual;
EXPRESSION
------------------------------
((4+5)+(6-7))
SQL Fiddle.
You can strip the outer parentheses with trim or regexp_replace if you want to, but they may be acceptable.
If you add another layer, say a record with values 7, 8, 9, '*', this would give:
EXPRESSION
------------------------------
((4+5)+(6-(8*9)))
SQL Fiddle.
But this isn't going to be very efficient against a large data set, since it will do a lot of single-row look-ups.

SQL Scalar function element not recognized in TSQL program

I have an input db2 table with two elements: loan_number, debt_to_income; this table's name is #Input_Table. I am trying to run a test the function by running a SQL program against this table. The problem is that the function's element is not being recognized in the SQL program for some reason, maybe I have been looking at to long? I need to validate that the output in the table will output in a order by the debt_to_income field.
Here is the function code:
ALTER FUNCTION [dbo].[FN_DTI_BANDS]
(
-- the parameters for the function here
#FN_DTI_Band decimal(4,3)
)
RETURNS varchar(16)
AS
BEGIN
declare #Return varchar(16)
select #Return =
Case
when #FN_DTI_Band is NULL then ' Missing'
WHEN #FN_DTI_Band = 00.00 then ' Missing'
When #FN_DTI_Band < = 0.31 then 'Invalid'
When #FN_DTI_Band between 0.31 and 0.34 then '31-34'
When #FN_DTI_Band between 0.34 and 0.38 then '34-38'
When #FN_DTI_Band >= 0.38 then '38+'
else null end
-- Return the result of the function
RETURN #Return
END
Here is the T-SQL test program:
SELECT loan_number,dbo.FN_DTI_BANDS(debt_to_income)as FN_DTI_Band
from #Input_table
SELECT COUNT(*), FN_DTI_Band
FROM #Input_table
GROUP BY FN_DTI_Band
ORDER BY FN_DTI_Band
Here is the error:
Msg 207, Level 16, State 1, Line 7
Invalid column name 'FN_DTI_Band'.
Msg 207, Level 16, State 1, Line 5
Invalid column name 'FN_DTI_Band'.
Can someone help me spot what I am overlooking? Thank you!
the table #input_table does not have a column called FN_DTI_Band.
Just the result of the first select statement has that column name.
You need to make the first select statement a sub query of the 2nd
Something like this:
SELECT COUNT(*), T.FN_DTI_Band
FROM
(
SELECT loan_number,dbo.FN_DTI_BANDS(debt_to_income) as FN_DTI_Band
from #Input_table
) T
GROUP BY T.FN_DTI_Band
ORDER BY T.FN_DTI_Band
Try prepending "dbo" onto the name of the function.
Select Count(*), dbo.FN_DTI_Band
From....