Oracle insert() function like mysql's - sql

Does oracle have an equivalent function like mysql's Insert() to handle strings ?
EDIT:
This is the coded answer in order to be easily understood
create or replace function fn_insert(ori_string in varchar2, in_pos in number,
p_length in number, new_string in varchar2)
return varchar2
is
resul varchar2(250) default '';
begin
if in_pos < 0 then
resul := ori_string;
else
resul := substr(ori_string, 1, in_pos-1)||new_string||substr(ori_string, in_pos+p_length, length(ori_string) - ((in_pos+p_length)-1));
end if;
return resul;
END fn_insert;
/

You can do it manually. So, insert(ori_string, in_pos, length, new_string) could be written as:
substr(ori_string, 1, in_pos)||new_string||substr(orig_string, in_pos+length, length(orig_string) - (in_pos+length))
In other databases, this function is also called stuff()

Related

How to implement a function that check a condition

I can't use boolean in a sql query.
Therefore I can't create a function that return true or false and use it to test a condition.
I must create a function that return something (1 for instance) and test it. Like that:
WITH
FUNCTION f (input INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN CASE WHEN input = 1 THEN 1 ELSE 0 END;
END;
A AS (SELECT 1 a FROM DUAL)
SELECT *
FROM a
WHERE f(a.a) = 1
instead of that:
WITH
FUNCTION f (input INTEGER)
RETURN boolean
IS
BEGIN
RETURN input = 1 ;
END;
A AS (SELECT 1 a FROM DUAL)
SELECT *
FROM a
WHERE f(a.a)
Unless there is another way?
code
I've tried to use a macro but to no avail
WITH
FUNCTION ft
RETURN VARCHAR2 SQL_MACRO
IS
BEGIN
RETURN q'{
SELECT 1
FROM dual
}';
END;
FUNCTION fc
RETURN VARCHAR2 SQL_MACRO
IS
BEGIN
RETURN q'{
1=1
}';
END;
SELECT *
FROM ft()
WHERE fc()
ORA-00920: invalid relational operator
code
The BOOLEAN data type is a PL/SQL only data type and is not supported in Oracle SQL statements.
Use two constants:
0 for false, 1 (or non-zero) for true (as per the C language).
0 for no errors, non-zero for errors (as per Unix program exit codes).
'Y' for yes, 'N' for no.
'success' and 'failure'
etc.
Whatever you return is your personal preference. Document the convention you are going to use and then use it consistently so that everyone on the same project uses the same convention.
Unless there is another way?
No, just pick a convention for truthy/falsy values and stick to that.
WITH
FUNCTION f_check_int ( p_str VARCHAR2 )
RETURN VARCHAR2 ------- Y / N
IS
lv_data NUMBER;
BEGIN
lv_data := TO_NUMBER(p_str);
IF lv_data>0 AND MOD(lv_data,1)=0 THEN
RETURN 'Y';
ELSE
RETURN 'N';
END IF;
EXCEPTION
WHEN VALUE_ERROR THEN
RETURN 'N';
END;
A AS (SELECT 1 a FROM DUAL)
SELECT *
FROM a
WHERE f_check_int(a.a) = 'Y'

Custom SQL function - PL/SQL: Statement ignored

Hi made a function that modifies a little bit the INSTR function (Oracle SQL):
instead of getting '0' when a space char isn't found, I get the length of the string.
It compiles but when I try to execute it, i'm getting the ORA-06550 error (which is, if i'm not wrong, a compilation error).
I tried this function as a procedure and it runs all right... so I don't understand why it doesn't want to execute.
Can you help me?
CREATE OR REPLACE FUNCTION "INSTR2" (str varchar2) RETURN NUMBER AS
pos number(4,0) := 0;
BEGIN
select INSTR(str, ' ') into pos from dual;
if (pos = 0) then
select to_number(length(str)) into pos from dual;
return pos;
else
return pos;
end if;
END INSTR2;
Thanks a lot,
R.L.
Well, there is already a built-in function called INSTR2 in Oracle (look, here it is).
I've just compiled your function with another name and it worked. So the code is valid, you just have to pick the name for it.
Apparently Oracle resolves INSTR2 to the built-in function despite the fact you now have the function with such name in your own schema (I couldn't find the reason for it in docs, but this behaviour is not exactly surprising). And INSTR2 expects more parameters than your implementation. That causes a runtime error.
Change you function name for excample to INSTR_SPACE and everething there will be ok, because in oracle there is a instr2 function and you will have problem during calling
Change your function body to
create or replace
function INSTR_SPACE(str varchar2) return number as
pos number(4, 0) := 0;
begin
pos := INSTR(str, ' ');
if (pos = 0) then
return to_number(length(str));
else
return pos;
end if;
end INSTR_SPACE;
you dont need sql operations, you can do it by simple pl/sql operations and it will be more faster

How get all matching positions in a string?

I have a column flag_acumu in a table in PostgreSQL with values like:
'SSNSSNNNNNNNNNNNNNNNNNNNNNNNNNNNNSNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN'
I need to show all positions with an 'S'. With this code, I only get the first such position, but not the later ones.
SELECT codn_conce, flag_acumu, position('S' IN flag_acumu) AS the_pos
FROM dh12
WHERE position('S' IN flag_acumu) != 0
ORDER BY the_pos ASC;
How to get all of them?
In Postgres 9.4 or later you can conveniently use unnest() in combination with WITH ORDINALITY:
SELECT *
FROM dh12 d
JOIN unnest(string_to_array(d.flag_acumu, NULL))
WITH ORDINALITY u(elem, the_pos) ON u.elem = 'S'
WHERE d.flag_acumu LIKE '%S%' -- optional, see below
ORDER BY d.codn_conce, u.the_pos;
This returns one row per match.
WHERE d.flag_acumu LIKE '%S%' is optional to quickly eliminate source rows without any matches. Pays if there are more than a few such rows.
Detailed explanation and alternatives for older versions:
PostgreSQL unnest() with element number
Since you didn't specify your needs to a point in which one could answer properly, I'm going with my assumption that you want a list of positions of occurence of a substring (can be more than 1 character long).
Here's the function to do that using:
FOR .. LOOP control structure,
function substr(text, int, int).
CREATE OR REPLACE FUNCTION get_all_positions_of_substring(text, text)
RETURNS text
STABLE
STRICT
LANGUAGE plpgsql
AS $$
DECLARE
output_text TEXT := '';
BEGIN
FOR i IN 1..length($1)
LOOP
IF substr($1, i, length($2)) = $2 THEN
output_text := CONCAT(output_text, ';', i);
END IF;
END LOOP;
-- Remove first semicolon
output_text := substr(output_text, 2, length(output_text));
RETURN output_text;
END;
$$;
Sample call and output
postgres=# select * from get_all_positions_of_substring('soklesocmxsoso','so');
get_all_positions_of_substring
--------------------------------
1;6;11;13
This works too. And a bit faster I think.
create or replace function findAllposition(_pat varchar, _tar varchar)
returns int[] as
$body$
declare _poslist int[]; _pos int;
begin
_pos := position(_pat in _tar);
while (_pos>0)
loop
if array_length(_poslist,1) is null then
_poslist := _poslist || (_pos);
else
_poslist := _poslist || (_pos + _poslist[array_length(_poslist,1)] + 1);
end if;
_tar := substr(_tar, _pos + 1, length(_tar));
_pos := position(_pat in _tar);
end loop;
return _poslist;
end;
$body$
language plpgsql;
Will return a position list which is an int array.
{position1, position2, position3, etc.}

How to cut varchar/text before n'th occurence of delimiter? PostgreSQL

I have strings (saved in database as varchar) and I have to cut them just before n'th occurence of delimiter.
Example input:
String: 'My-Example-Awesome-String'
Delimiter: '-'
Occurence: 2
Output:
My-Example
I implemented this function for fast prototype:
CREATE OR REPLACE FUNCTION find_position_delimiter(fulltext varchar, delimiter varchar, occurence integer)
RETURNS varchar AS
$BODY$
DECLARE
result varchar = '';
arr text[] = regexp_split_to_array( fulltext, delimiter);
word text;
counter integer := 0;
BEGIN
FOREACH word IN ARRAY arr LOOP
EXIT WHEN ( counter = occurence );
IF (counter > 0) THEN result := result || delimiter;
END IF;
result := result || word;
counter := counter + 1;
END LOOP;
RETURN result;
END;
$BODY$
LANGUAGE 'plpgsql' IMMUTABLE;
SELECT find_position_delimiter('My-Example-Awesome-String', '-', 2);
For now it assumes that string is not empty (provided by query where I will call function) and delimiter string contains at least one delimiter of provided pattern.
But now I need something better for performance test. If it is possible, I would love to see the most universal solution, because not every user of my system is working on PostgreSQL database (few of them prefer Oracle, MySQL or SQLite), but it is not the most importatnt. But performance is - because on specific search, that function can be called even few hundreds times.
I didn't find anything about fast and easy using varchar as a table of chars and checking for occurences of delimiter (I could remember position of occurences and then create substring from first char to n'th delimiter position-1). Any ideas? Are smarter solutions?
# EDIT: yea, I know that function in every database will be a bit different, but body of function can be very similliar or the same. Generality is not a main goal :) And sorry for that bad function working-name, I just saw it has not right meaning.
you can try doing something based on this:
select
varcharColumnName,
INSTR(varcharColumnName,'-',1,2),
case when INSTR(varcharColumnName,'-',1,2) <> 0
THEN SUBSTR(varcharColumnName, 1, INSTR(varcharColumnName,'-',1,2) - 1)
else '...'
end
from tableName;
of course, you have to handle "else" the way you want. It works on postgres and oracle (tested), it should work on other dbms's because these are standard sql functions
//edit - as a function, however this way it's rather hard to make it cross-dbms
CREATE OR REPLACE FUNCTION find_position_delimiter(fulltext varchar, delimiter varchar, occurence integer)
RETURNS varchar as
$BODY$
DECLARE
result varchar := '';
delimiterPos integer := 0;
BEGIN
delimiterPos := INSTR(fulltext,delimiter,1,occurence);
result := SUBSTR(fulltext, 1, delimiterPos - 1);
RETURN result;
END;
$BODY$
LANGUAGE 'plpgsql' IMMUTABLE;
SELECT find_position_delimiter('My-Example-Awesome-String', '-', 2);
create or replace function trunc(string text, delimiter char, occurence int) returns text as $$
return delimiter.join(string.split(delimiter)[:occurence])
$$ language plpythonu;
# select trunc('My-Example-Awesome-String', '-', 2);
trunc
------------
My-Example
(1 row)

truncated LISTAGG string [duplicate]

This question already has answers here:
LISTAGG function: "result of string concatenation is too long"
(14 answers)
Closed 8 years ago.
I'm using Oracle 11g r2 and I need to concatenate strings (VARCHAR2, 300) from multiple rows. I'm using LISTAGG which works great until the concatenated string reaches the limit. At that point I receive a ORA-01489: result of string concatenation is too long.
In the end, I only want the first 4000 chars of the concatenated string. How I get there doesn't matter. I will accept inefficient solutions.
Here's my query:
SELECT LISTAGG(T.NAME, ' ') WITHIN GROUP (ORDER BY NULL)
FROM T
This code works for any length of data, fast enough
SELECT REPLACE(
REPLACE(
XMLAGG(
XMLELEMENT("A",T.NAME)
ORDER BY 1).getClobVal(),
'<A>',''),
'</A>','[delimiter]')
FROM T
You can either use the built-in (but deprecated) STRAGG function
select sys.stragg(distinct name) from t
(please note that distinct seems to be necessary to avoid duplicates)
or define your own aggregation function / type:
CREATE OR REPLACE TYPE "STRING_AGG_TYPE" as object
(
total varchar2(4000),
static function ODCIAggregateInitialize(sctx IN OUT string_agg_type) return number,
member function ODCIAggregateIterate(self IN OUT string_agg_type,
value IN varchar2) return number,
member function ODCIAggregateTerminate(self IN string_agg_type,
returnValue OUT varchar2,
flags IN number) return number,
member function ODCIAggregateMerge(self IN OUT string_agg_type,
ctx2 IN string_agg_type) return number
);
CREATE OR REPLACE TYPE BODY "STRING_AGG_TYPE" is
static function ODCIAggregateInitialize(sctx IN OUT string_agg_type) return number is
begin
sctx := string_agg_type(null);
return ODCIConst.Success;
end;
member function ODCIAggregateIterate(self IN OUT string_agg_type,
value IN varchar2) return number is
begin
-- prevent buffer overflow for more than 4,000 characters
if nvl(length(self.total),
0) + nvl(length(value),
0) < 4000 then
self.total := self.total || ';' || value;
end if;
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate(self IN string_agg_type,
returnValue OUT varchar2,
flags IN number) return number is
begin
returnValue := ltrim(self.total,
';');
return ODCIConst.Success;
end;
member function ODCIAggregateMerge(self IN OUT string_agg_type,
ctx2 IN string_agg_type) return number is
begin
self.total := self.total || ctx2.total;
return ODCIConst.Success;
end;
end;
CREATE OR REPLACE FUNCTION stragg(input varchar2 )
RETURN varchar2
PARALLEL_ENABLE AGGREGATE USING string_agg_type;
and use it like this:
select STRAGG(name) from t
I believe this approach was orginally proposed by Tom Kyte (at least, that's where I got it from - Asktom: StringAgg
Maybe it will help you:
substr(string, 1, 4000)
EDIT:
or try
SELECT [column], rtrim(
xmlserialize(content
extract(
xmlagg(xmlelement("n", (T.NAME||',') order by [column])
, '//text()'
)
)
, ','
) as list
FROM [table]
GROUP BY [column]
;
This is the drawback of the LISTAGG function ,it does not handle the limit of the string generated due to LISTAGG analytical function .For that you need to take the cumulative sum of length and based on that you need to limit .
worked out example on emp table
select listagg(ename,' ')within group (order by null)
from
(
select ename,
sum( length( ename ) + 1)
over ( order by ename rows between unbounded preceding and current row) length
from emp
)where lngth <= 4000
;
But this will not give the perfect result because if you look to the inner query ,it will generate a column with ename and its length as shown below
ename lenght
===================
gaurav 6
rohan 11
:
:
garima 3996
anshoo 4002
=====================
So the above function will give you result till garima ....not till ansh,because listagg is based on the length column of inner query .