Converting function instr from Oracle to PostgreSQL (sql only) - postgresql

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

Related

What is PostgreSQL equivalent for MSSQL STR ( float_expression [ , length [ , decimal ] ] ) function?

In MSSQL STR returns character data converted from numeric data. The character data is right-justified, with a specified length and decimal precision. SELECT STR(123.45, 6, 1) return ' 123.5' (not '123.5') in MSSQL. Also, SELECT STR(123.45, 2, 2) return '**' in MSSQL.
There is no direct corresponding Postgres function. You have a couple things going on: rounding at variable length, padding the result and result length validation. But easy enough to create your own SQL function: (see demo)
create or replace
function str(value_in numeric
,length_in integer default 7
,dec_len_in integer default 2
)
returns text
language sql
as $$
select case when length(val) > length_in
then rpad('*',length_in,'*') -- or just '**'
else lpad(val,length_in,'#') -- for demo using #, for real use ' '
end
from (select round(value_in, dec_len_in )::text) sq(val)
$$;

getting formatted column value in postgres

I have a column in my table having values of format:
COURSE_214/MODULE_5825/SUBMODULE_123/..../GOAL_124/ACTIVITY_125.
I need value for goal i.e 124 from goal_124. I am planning to use 'regexp_split_to_array' but don't know how to use elements from array.
I am using postgres 9.2.
You can use split_part like so:
select split_part(split_part('COURSE_214/MODULE_5825/SUBMODULE_123/..../GOAL_124/ACTIVITY_125', '/GOAL_', 2), '/', 1)
i.e.
select split_part(split_part(fieldname, '/GOAL_', 2), '/', 1)
Result:
124
Using json_object():
select json_object(string_to_array(translate(params, '_', '/'), '/'))
from test
json_object
------------------------------------------------------------------------------------------------
{"COURSE" : "214", "MODULE" : "5825", "SUBMODULE" : "123", "GOAL" : "124", "ACTIVITY" : "125"}
(1 row)
select json_object(string_to_array(translate(params, '_', '/'), '/'))->>'GOAL' as goal
from test
goal
------
124
(1 row)
The column has a format suitable for json. I would suggest to change the type of the column to jsonb. The first query may be used as a converter.
After the conversion you would access the parameters in an easy way, e.g.:
select *
from test
where params->>'COURSE' = '214'
and (params->>'GOAL')::int > 120;
Simple select of all GOAL_ parameters (if there are more than one)
select ltrim(elem, 'GOAL_')
from (
select unnest(string_to_array(params, '/')) elem
from test
) sub
where elem like 'GOAL_%'
You may try using regular expressions, getting the string between slashes
select substring(your_column from '^.*/(.*)/.*$') from your_table
If you expect to find in that part the GOAL value, use
select substring(your_column from '/GOAL_(.*)/') from your_table

Use REGEXP_SUBSTR in 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'.

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 Between Alphanumeric value

I have an Alphanumeric column in my db table. For my filter, I was using between to get the result filter value. Everything is okay. But, In some cases it misses some of the data's from filtering. Here are my samples,
Sample data
ACQPO14
002421
ACQPO8
ACQPO14
ACQPO19
DUMMY0001
Sql Query
SELECT po.No,
po.PoS
FROM PoDetails pod
INNER JOIN Pors po ON po.Id=PoD.PoId
WHERE po.No BETWEEN 'ACQPO1' AND 'ACQPO20'
For the above sample. the query returns only ACQPO14 and ACQPO19 NOT ACQPO8.
Any help to this issue will be appreciated.
Thanks
It makes sense as it is just text.
1 comes before 8 so, ordering in text (left to right) the db will disregard the last digit of ACQPO14 to compare it against ACQPO8. So ACQPO1 (4 removed) comes before ACQPO8 and ACQPO2 (0 removed) comes before ACQPO8 as well. So it gets filtered out by the between.
The only way for you to fix this is to parse the column by splitting it. EG: If ACQPO is a fixed-length prefix you can use some DBMS function (you haven't specified any) to trim that part and turn into a numeric format the rest. Then compare/filter by that numeric remainder.
This is how you would do this in Oracle
SELECT po.No, po.PoS
FROM PoDetails pod
INNER JOIN Pors po ON po.Id=PoD.PoId
WHERE SUBSTR(po.No, 1, 5) = 'ACPQO'
AND TO_NUMBER(SUBSTR(po.No, 6, 2)) >= 1
AND TO_NUMBER(SUBSTR(po.No, 6, 2)) <= 20;
First SUBSTR() is used to match the textual part of the value. Then the numeric part of the values is parsed into a number using TO_NUMBER() and made available for numeric comparison between 1 and 20. (Other databases would also have similar functions to do the same.)
If ACQPO is fixed then try below in SQL Server
SELECT po.No, po.PoS
FROM PoDetails pod
INNER JOIN Pors po ON po.Id=PoD.PoId
WHERE left(po.No,5) and
cast(substring(po.No,6,len(po.No)) as int)
BETWEEN cast(substring('ACQPO1',6,len(po.No)) as int) AND cast(substring('ACQPO20',6,len(po.No)) as int)
SELECT substring(data,6,len(data)),* FROM #Temp Where
left(data,5) ='ACQPO' And
cast(substring(data,6,len(data)) as int)
BETWEEN cast(substring('ACQPO1',6,len(data)) as int) AND cast(substring('ACQPO20',6,len(data)) as int)
FOR MYSQL:-
SELECT * FROM my_table WHERE
SUBSTR(seq_num, 1,3) = 'ABC' /* verifying prefix code */
AND
REPLACE(seq_num, 'ABC', '') >= 61440
AND
REPLACE(seq_num, 'ABC', '') <= 61807
OR
SELECT * FROM my_table WHERE
SUBSTR(seq_num, 1,3) = 'ABC' /* verifying prefix code */
AND
substr(seq_num, 4, length(seq_num)) >= 61440
AND
SUBSTR(seq_num, 4, LENGTH(seq_num)) <= 61807
works for (like) :
ABC61447,
ABC61448,
ABC61545,
...,
...,
ABC61807
Just use range 10 to 20 and it works!
SELECT po.No,
po.PoS
FROM PoDetails pod
INNER JOIN Pors po ON po.Id=PoD.PoId
WHERE po.No BETWEEN 'ACQPO10' AND 'ACQPO20'