Multiple inline functions in Oracle - sql

I have the requirement to call my 2nd inline function into my 1st function. I am not able to achieve this one and getting error only.
with
function add_string(p_string in varchar2) return varchar2
is
--Function to add a string
l_buffer varchar2(32767);
begin
l_buffer := p_string || ' works!';
--
return l_buffer;
--
end ;
--
function doesnt_it(p_string in varchar2) return varchar2
is
l_buffer varchar2(32767);
pp_string varchar2(32767);
begin
select add_string(p_string) into pp_string from dual;
l_buffer := pp_string || ' Doesnt it?';
--
return l_buffer;
--
end ;
--
select doesnt_it('Yes, it') as outVal
from dual
;

Yes it is possible you should replace SELECT func() INTO variable with direct function call:
ORA-06553: PLS-231: function 'ADD_STRING' may not be used in SQL
with
function add_string(p_string in varchar2) return varchar2
is
--Function to add a string
l_buffer varchar2(32767);
begin
l_buffer := p_string || ' works!';
--
return l_buffer;
--
end ;
function doesnt_it(p_string in varchar2) return varchar2
is
l_buffer varchar2(32767);
-- pp_string varchar2(32767);
begin
-- select add_string(p_string) into pp_string from dual;
l_buffer := add_string(p_string) || ' Doesnt it?'; -- here is function call
--
return l_buffer;
--
end ;
select doesnt_it('Yes, it') as outVal
from dual;
db<>fiddle demo
Output:
OUTVAL
Yes, it works! Doesnt it?
Alternative solution:
pp_string := add_string(p_string);
l_buffer := pp_string || ' Doesnt it?';
db<>fiddle demo2
You could also have inline function/procedure in inline with block Procedures in the WITH Clause

Related

Adding output to function PL/SQL

I am trying to get my function to include the date and current user, but I keep getting an error that my function is in an invalid state. If I comment out or remove the two dbms_output... lines it works just fine. Any ideas of how to return that output? Using Oracle SQL Developer
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
str1 VARCHAR2(10) := x;
str2 VARCHAR2(10) := y;
BEGIN
RETURN str1 || str2;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || SYSDATE || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
END;
/
SELECT f_concatenate_strings('Crypto','Currency') FROM DUAL;
You have to declare the result Variable first, also enable the dbms_output on your SQL Developer.
This should work.
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
str1 VARCHAR2(10) := x;
STR2 VARCHAR2(10) := Y;
result VARCHAR2(250);
BEGIN
result := str1 || str2;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || SYSDATE || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
RETURN result;
END;
/
SELECT F_CONCATENATE_STRINGS('Crypto','Currency') FROM DUAL;
your function has some issues.
result is not declared
return statement should be the last statement in the function. it return a result and terminates the execution of the function
you should convert to char the sysdate
you can try this one:
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
--str1 VARCHAR2(10) := x;
--str2 VARCHAR2(10) := y;
result VARCHAR2(20);
BEGIN
result := x || y;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || to_char(SYSDATE,'dd.mm.yyyy') || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
RETURN result; -- move to the end of the function
END;
/
SELECT f_concatenate_strings('Crypto','Currency') FROM DUAL;

extract a string with a string quote issue pl sql

I used the function below to extract a string from a character string in input,the issue is that when i put a string character with a string quote i can't extract it
exp as input :
AA015streetl'adeuilAB00201AC0041234AD012XXXXXXXXXXXXAE009TTTTTTTTT
FUNCTION get_string (p_name IN VARCHAR2,
p_strg IN VARCHAR2,
p_len OUT NOCOPY PLS_INTEGER,
p_value OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER
IS
v_counter PLS_INTEGER := 1;
v_strg VARCHAR2 (4096) := SUBSTR (p_strg, 5);
BEGIN
p_value := NULL;
p_len := 0.;
WHILE v_counter < LENGTH (v_strg)
LOOP
IF SUBSTR (v_strg, v_counter, 3.) = p_name
THEN
p_len :=
TO_NUMBER (SUBSTR (v_strg, v_counter + 3., 3.));
p_value :=
SUBSTR (v_strg, v_counter + 6., p_len);
RETURN (declaration_cst.ok);
END IF;
v_counter :=
v_counter
+ 6.
+ TO_NUMBER (SUBSTR (v_strg, v_counter + 3., 3.));
END LOOP;
RETURN (declaration_cst.nok);
END;
END get_string;
For the example string you showed, the substring offsets and counter adjustments in your code are off. This can extract any of the 'tags':
CREATE OR REPLACE FUNCTION get_string (p_name IN VARCHAR2, p_strg IN VARCHAR2,
p_len OUT NOCOPY PLS_INTEGER, p_value OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER IS
v_counter PLS_INTEGER := 1;
BEGIN
p_value := NULL;
p_len := 0;
WHILE v_counter < LENGTH (p_strg)
LOOP
IF SUBSTR (p_strg, v_counter, 2) = p_name
THEN
p_len := TO_NUMBER (SUBSTR (p_strg, v_counter + 2, 3));
p_value := SUBSTR (p_strg, v_counter + 5, p_len);
RETURN declaration_cst.ok;
END IF;
v_counter := v_counter + 5
+ TO_NUMBER (SUBSTR (p_strg, v_counter + 2, 3));
END LOOP;
RETURN declaration_cst.nok;
END get_string;
/
Your version was losing the first four characters completely in the v_strg assignment, and was then adjusting as if the tags were three characters, not two.
With a test block like:
set serveroutput on size unlimited
declare
str varchar2(256) := q'[AA015street l'adeuilAB00201AC0041234AD012XXXXXXXXXXXXAE009TTTTTTTTT]';
len pls_integer;
value varchar2(256);
rc pls_integer;
begin
rc := get_string('AB', str, len, value);
dbms_output.put_line('AB -> ' || rc ||':'|| len ||':'|| value);
end;
/
and changing the 'AB' tags this gets:
AA -> 0:15:street l'adeuil
AB -> 0:2:01
AC -> 0:4:1234
AD -> 0:12:XXXXXXXXXXXX
AE -> 0:9:TTTTTTTTT
AF -> 1:0:
SQL Fiddle demo with wrapper function so the extracted tag info can be shown more easily.
It doesn't make any difference if the string value contains a single quote, as long as the tag length is correct - it's just another character to substr and won't be interpreted any differently.
As Bob Jarvis mentioned, there are other ways to achieve this breakdown, but that's even more beyond the scope of what you asked.
When you have a single-quote embedded in a string literal you're trying to pass to a function or procedure you'll need to double it (i.e. type two single-quotes instead of one) so that PL/SQL interprets it correctly. The doubled single-quote will be sent to the function as just one single quote, as you intend, but this is how single-quotes in literals are handled in PL/SQL. Thus, instead of calling your function as
n := get_string ('AB',
'AA015streetl'adeuilAB00201AC0041234AD012XXXXXXXXXXXXAE009TTTTTTTTT',
p_len,
p_value );
you should double the single-quote in the second parameter:
n := get_string ('AB',
'AA015streetl''adeuilAB00201AC0041234AD012XXXXXXXXXXXXAE009TTTTTTTTT',
p_len,
p_value );
If you're reading the input from a file or some other external source you don't have to do this; it's only necessary when a string literal has a single-quote in it.
Share and enjoy.

to create a particular procedure [duplicate]

How to use put function.my procedure is not compiling with put. but putline is working fine. i want to print in the same line
Here's an example of code which uses the UTL_FILE.PUT and UTL_FILE.PUT_LINE calls:
declare
fHandle UTL_FILE.FILE_TYPE;
begin
fHandle := UTL_FILE.FOPEN('my_directory', 'test_file', 'w');
UTL_FILE.PUT(fHandle, 'This is the first line');
UTL_FILE.PUT(fHandle, 'This is the second line');
UTL_FILE.PUT_LINE(fHandle, 'This is the third line');
UTL_FILE.FCLOSE(fHandle);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Exception: SQLCODE=' || SQLCODE || ' SQLERRM=' || SQLERRM);
RAISE;
end;
The output from this looks like:
This is the first lineThis is the second lineThis is the third line
Share and enjoy.
Here is a robust function for using UTL_File.putline that includes the necessary error handling. It also handles headers, footers and a few other exceptional cases.
PROCEDURE usp_OUTPUT_ToFileAscii(p_Path IN VARCHAR2, p_FileName IN VARCHAR2, p_Input IN refCursor, p_Header in VARCHAR2, p_Footer IN VARCHAR2, p_WriteMode VARCHAR2) IS
vLine VARCHAR2(30000);
vFile UTL_FILE.file_type;
vExists boolean;
vLength number;
vBlockSize number;
BEGIN
UTL_FILE.fgetattr(p_path, p_FileName, vExists, vLength, vBlockSize);
FETCH p_Input INTO vLine;
IF p_input%ROWCOUNT > 0
THEN
IF vExists THEN
vFile := UTL_FILE.FOPEN_NCHAR(p_Path, p_FileName, p_WriteMode);
ELSE
--even if the append flag is passed if the file doesn't exist open it with W.
vFile := UTL_FILE.FOPEN(p_Path, p_FileName, 'W');
END IF;
--GET HANDLE TO FILE
IF p_Header IS NOT NULL THEN
UTL_FILE.PUT_LINE(vFile, p_Header);
END IF;
UTL_FILE.PUT_LINE(vFile, vLine);
DBMS_OUTPUT.PUT_LINE('Record count > 0');
--LOOP THROUGH CURSOR VAR
LOOP
FETCH p_Input INTO vLine;
EXIT WHEN p_Input%NOTFOUND;
UTL_FILE.PUT_LINE(vFile, vLine);
END LOOP;
IF p_Footer IS NOT NULL THEN
UTL_FILE.PUT_LINE(vFile, p_Footer);
END IF;
CLOSE p_Input;
UTL_FILE.FCLOSE(vFile);
ELSE
DBMS_OUTPUT.PUT_LINE('Record count = 0');
END IF;
EXCEPTION
WHEN UTL_FILE.INVALID_PATH THEN
DBMS_OUTPUT.PUT_LINE ('invalid_path');
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
WHEN UTL_FILE.INVALID_MODE THEN
DBMS_OUTPUT.PUT_LINE ('invalid_mode');
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
WHEN UTL_FILE.INVALID_FILEHANDLE THEN
DBMS_OUTPUT.PUT_LINE ('invalid_filehandle');
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
WHEN UTL_FILE.INVALID_OPERATION THEN
DBMS_OUTPUT.PUT_LINE ('invalid_operation');
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
WHEN UTL_FILE.READ_ERROR THEN
DBMS_OUTPUT.PUT_LINE ('read_error');
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
WHEN UTL_FILE.WRITE_ERROR THEN
DBMS_OUTPUT.PUT_LINE ('write_error');
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
WHEN UTL_FILE.INTERNAL_ERROR THEN
DBMS_OUTPUT.PUT_LINE ('internal_error');
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('other write error');
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
END;
You can check the below example of how to log/write using utl_file in PL/SQL.
DECLARE
fhandle utl_file.file_type;
BEGIN
fhandle := utl_file.fopen(
'UTL_DIR_STACKOVERFLOW'-- File location
, 'Check_Logging.txt' -- File name
, 'a' -- Open mode: a = append, w = write
);
utl_file.put(fhandle, 'Stackoverflow' );
utl_file.fclose(fhandle);
END;
Problems you can face-
Invalid directory object
Then you can create your own directory object and use the below query.
create or replace directory UTL_DIR_STACKOVERFLOW as '/tmp/';
CREATE OR REPLACE PROCEDURE SP_EXPORT_TO_CSV(P_OWNER VARCHAR2,
P_OBJECT_NAME VARCHAR2,
P_FILE_NAME VARCHAR2 --,
-- P_DELIMITED CHAR
) AS
-- declaration
TYPE C_DATA_CURSOR IS REF CURSOR;
C_DATA C_DATA_CURSOR;
v_file UTL_FILE.FILE_TYPE;
V_COLUMNS VARCHAR2(32767);
X VARCHAR2(32767);
V_FILE_NAME VARCHAR2(2000);
V_OWNER VARCHAR2(100);
V_OBJECT_NAME VARCHAR2(1000);
V_SQL VARCHAR2(32767);
V_DELIMITED VARCHAR2(10);
BEGIN
-- set value
V_FILE_NAME := P_FILE_NAME;
V_OBJECT_NAME := P_OBJECT_NAME;
V_OWNER := P_OWNER;
--V_DELIMITED:=P_DELIMITED;
SELECT REPLACE(LISTAGG(COLUMN_NAME, '|') WITHIN
GROUP(ORDER BY COLUMN_ID),
'|',
' || ' || '''|''' || ' || ')
INTO V_COLUMNS
FROM ALL_TAB_COLUMNS W
WHERE W.TABLE_NAME = V_OBJECT_NAME
AND W.OWNER = V_OWNER
ORDER BY COLUMN_ID;
-- SET OUTPUT PARAMETER --'EXPORTED_TO_TEXT_LOCATION'
v_file := UTL_FILE.FOPEN(location => 'MYLOCATION',
filename => V_FILE_NAME,
open_mode => 'w',
max_linesize => 32767);
UTL_FILE.PUT_LINE(v_file, REPLACE(V_COLUMNS, ' || ''|'' || ', '|'));
X := 'SELECT ' || V_COLUMNS || ' AS RECORD FROM ' || V_OWNER || '.' ||
V_OBJECT_NAME;
OPEN C_DATA FOR X;
LOOP
FETCH C_DATA
INTO V_SQL;
EXIT WHEN C_DATA%NOTFOUND;
UTL_FILE.PUT_LINE(v_file, V_SQL);
END LOOP;
CLOSE C_DATA;
UTL_FILE.FCLOSE(v_file);
END;

Splitting two semicolons separated params

How to get two values from string in PL/SQL, like this:
DECLARE
context VARCHAR2(50) := 'param_a=Value1;param_b=Value2,Value3;';
paramA VARCHAR(50);
paramB VARCHAR(50);
BEGIN
paramA = ... -- expected value: Value1
paramB = ... -- expected value: Value2,Value3
dbms_output.put_line(context);
END;
You can use something like:
DECLARE
context VARCHAR2(50) := 'param_a=Value1;param_b=Value2,Value3;';
paramA VARCHAR(50);
paramB VARCHAR(50);
BEGIN
paramA := SUBSTR('param_a=Value1;param_b=Value2,Value3;',instr('param_a=Value1;param_b=Value2,Value3;','=',1,1)+1,instr('param_a=Value1;param_b=Value2,Value3;',';',1,1)-instr('param_a=Value1;param_b=Value2,Value3;','=',1,1)-1);
paramB := SUBSTR('param_a=Value1;param_b=Value2,Value3;',instr('param_a=Value1;param_b=Value2,Value3;','=',1,2)+1,instr('param_a=Value1;param_b=Value2,Value3;',';',1,2)-instr('param_a=Value1;param_b=Value2,Value3;','=',1,2)-1);
dbms_output.put_line(paramA);
dbms_output.put_line(paramb);
END;
Hope it Helps
Vishad
DECLARE
type TStrings IS TABLE OF VARCHAR2(4000);
ltParams TStrings;
ltParamsValue TStrings;
function split(ivStr varchar2, ivDelim varchar2 := ',') return TStrings
IS
ltStrings TStrings;
lvStr varchar2(4000);
i number;
BEGIN
lvStr := ivStr;
ltStrings := TStrings();
loop
i := instr(lvStr, ivDelim);
i := case when i = 0 and lvStr is not null then length(lvStr) else i end;
exit when i = 0 or lvStr is null;
ltStrings.extend(1);
ltStrings(ltStrings.count) := rtrim(substr(lvStr, 1, i), ivDelim);
lvStr := substr(lvStr, i + 1);
end loop;
RETURN ltStrings;
END;
BEGIN
ltParams := split('param_a=Value1;param_b=Value2,Value3;', ';');
FOR idx IN ltParams.first .. ltParams.last LOOP
ltParamsValue := split(ltParams(idx), '=');
dbms_output.put_line(ltParamsValue(2));
END LOOP;
end;
Hi Better way to do this kind of separation is by using REGEX(Regular Expressions).
PLease try this code it may help plus it will reduce the coding length too.
SET serveroutput ON;
DECLARE
context VARCHAR2(50) := 'param_a=Value1;param_b=Value2,Value3;';
paramA VARCHAR(50);
paramB VARCHAR(50);
BEGIN
FOR rec IN
(SELECT regexp_substr(context,'[^;"]+', 1, level) AS AV_TEST
FROM dual
CONNECT BY regexp_substr(context,'[^;"]+', 1, level) IS NOT NULL
)
LOOP
dbms_output.put_line(rec.av_test);
END LOOP;
END;

Selection of columns in stored procedure based on parameters

I need to check the parameters in the stored procedure if it is entered then I need to select that
i=j=k=l=m=1;
IF (p_plant_cd IS NULL) THEN
i=0;
END IF;
IF(p_global_duns_nbr IS NULL) THEN
j=0
END IF;
IF(p_global_duns_nbr IS NULL) THEN
k=0
END IF;
IF(p_matrl_grp IS NULL) THEN
l=0
END IF;
IF (p_mrp IS NULL) THEN
m=0
END IF ;
Which ever value is 1 I need to add corresponding parameters in the variable v_select
For eg ;
if k and l are 1 then
v_select='p_global_duns_nbr,p_matrl_grp'
Pls suggest me how to do this.
You can declare a variable , holding column names , then concat to select statement and you have SQL select statement in v_select , use it as you want
declare
v_columns varchar2(255);
v_select varchar2(2000);
v_result sys_refcursor;
begin
v_columns := 'rownum';
IF p_plant_cd = 1 THEN
v_columns := v_columns || ',p_plant_cd';
END IF;
IF p_global_duns_nbr = 1 THEN
v_columns := v_columns || ',p_global_duns_nbr';
END IF;
IF p_global_duns_nbr = 1 THEN
v_columns := v_columns || ',p_global_duns_nbr';
END IF;
IF p_matrl_grp = 1 THEN
v_columns := v_columns || ',p_matrl_grp';
END IF;
IF p_mrp = 1 THEN
v_columns := v_columns || ',p_mrp';
END IF;
v_select := 'SELECT ' || v_columns || ' FROM table';
open v_result for v_select;
end;