CREATE OR REPLACE FUNCTION f_old_transactions (IN p_fromdate date, IN p_todate date, IN p_transtype varchar,IN OUT p_cancelled boolean,
OUT p_transaction_date date,
OUT p_type varchar,
OUT p_description varchar,
OUT p_amount numeric)
RETURNS SETOF record AS
$BODY$
declare lRunQuery text;
declare lTotalRec record;
declare lBranchList text[];
declare lTranstype text[];
BEGIN
select into lTranstype
dt.type
from
v_data_types dt;
lTranstype := regexp_split_to_array(p_transtype, ',');
lrunquery := 'select
it.transaction_date trandate,
dt.type,
it.description,
ita.amount,
it.cancelled
from
import_transaction it
inner join import_transaction_account ita on it.import_transaction_id=ita.import_transaction_id
where
it.transaction_date >= ' || quote_literal(p_fromdate) || '
and it.transaction_date <= ' || quote_literal(p_todate) || '
and dt.type = any(' || quote_literall(p_transtype) || ') and';
if (p_cancelled = TRUE) then
lrunquery := lrunquery || '
it.cancelled = ' || quote_literal(p_cancelled) || '';
else
lrunquery := lrunquery || '
it.cancelled = ' || quote_literal(p_cancelled) || '';
end if;
FOR lTotalrec in
execute lRunQuery
LOOP
p_transaction_date := ltotalrec.trandate;
p_type :=ltotalrec.type;
p_description :=ltotalrec.description;
p_amount :=ltotalrec.amount;
p_cancelled := ltotalrec.cancelled;
return next;
END LOOP;
return ;
end;
$BODY$
LANGUAGE plpgsql IMMUTABLE
COST 100
ROWS 1000;
ALTER FUNCTION f_old_transactions(date,date,varchar,boolean) OWNER TO "CompuLoanPostgres";
select * from f_old_transactions ('01-Jan-2010','31-Dec-2018','Receipt Cash','FALSE')
I'm getting an error that my array value must start with "{". My array I'm trying to create is from a view v_data_type the view consist of only one column with a varchar type.
Can anyone please direct me where the issue in my code is?
Thank you in advance
I don't think you've given us enough information to know for certain what's going wrong. Notably, I have no idea what the tables should look like, either in schema or content. Without that, I can't build a test case in my own DB to debug.
That said, I noticed a couple things, specifically around the lTranstype variable:
You're assigning lTranstype twice. First you SELECT INTO it, and then you immediately assign it to a value unpacked from the p_transtype argument. It's not clear to me what you want in that variable.
When constructing your query later, you include and dt.type = any(' || quote_literall(p_transtype) || '). The problem is that p_transtype is a varchar argument, and you're trying to access it like an array. I suspect you want that to read and dt.type = any(' || quote_literall(lTranstype) || '), but I could be mistaken.
I'm guessing that your type error is coming from that second problem, but it seems like you need to reassess what the different variables in this function are intended for. Good luck.
Related
I have the need to capture the o/p of "execute immediate" statement and use that o/p variable in incremental steps execution of the proc.
Like the code is :
var_01 := 'SELECT COUNT(1) AS cnt FROM ALL_RESOURCE_MONITOR_MASTER WHERE' || ' '
|| 'account_name = ' ||''''|| lv_acct_name_new ||''''|| ' ' || 'AND' || ' '
|| 'rm_type= ' ||''''|| type || ''''||' ' || 'AND' || ' '
|| 'env= ' ||''''|| env || ''''||' ';
cnt := (execute immediate :var_01);
Now post I get the cnt as populated, then I need to run the below if~else statement:
if (cnt > 0) then
return 'execute the statement';
else
return 'Resource monitor already present hence insert skipped';
end if;
But the problem is when I try to execute this block I am always getting an error like :
SQL compilation error: Invalid expression value (?SqlExecuteImmediateDynamic?) for assignment.
Please do provide your inputs on resolving this. Thanks in advance!!
Unfortunately there is no syntax like EXECTUE <sql> INTO <output_variable_list> USING <input_variable_list> known from different dialects.
EXECUTE IMMEDIATE:
Executes a string that contains a SQL statement or a Snowflake Scripting statement.
EXECUTE IMMEDIATE '<string_literal>'
[ USING (bind_variable_1 [, bind_variable_2 ...] ) ] ;
In this scenario there is no need to use dynamic SQL for checking in metadata table as it could be rewritten to use parametrized if-statement:
CREATE OR REPLACE TABLE ALL_RESOURCE_MONITOR_MASTER
AS
SELECT 'acc1' AS account_name, 'TEST' AS env, 'WAREHOUSE' AS rm_type;
Code:
DECLARE
lv_acct_name_new TEXT := 'acc1';
type TEXT := 'WAREHOUSE';
env TEXT := 'TEST';
BEGIN
IF (EXISTS (SELECT 1
FROM ALL_RESOURCE_MONITOR_MASTER
WHERE account_name = :lv_acct_name_new
AND rm_type = :type
AND env = :env
)) THEN
RETURN 'Executing some code';
ELSE
RETURN 'Resource monitor already present hence insert skipped';
END IF;
END;
I have built the following procedure to read data into a CLOB (see code example below).
Now I would like to have the same functionality to do that for a LONG column.
I know that LONGs are deprecated in ORACLE but for my particular case it is neccessary to stay with LONG (for the moment).
CREATE OR REPLACE PROCEDURE my.p_update_proc (
p_table_name IN VARCHAR2,
p_column_name IN VARCHAR2,
p_pattern IN VARCHAR2
)
as
l_sql varchar2(4000);
l_bfile bfile := bfilename('MY_DIR',p_table_name||'.'||p_pattern||'.txt');
l_data clob;
l_soffset NUMBER := 1;
l_doffset NUMBER := 1;
l_lang NUMBER := 0;
l_warn NUMBER;
BEGIN
DBMS_LOB.createtemporary (lob_loc => l_data,cache => TRUE,
dur => DBMS_LOB.call);
dbms_lob.fileopen( l_bfile, DBMS_LOB.file_readonly );
DBMS_LOB.loadclobfromfile(l_data, l_bfile, DBMS_LOB.LOBMAXSIZE,
l_soffset, l_doffset, 0, l_lang, l_warn );
dbms_lob.fileclose(l_bfile);
dbms_output.put_line(l_sql); -- to debug/trace your dynamic statement
l_sql := 'update ' || p_table_name || ' set '|| p_column_name || '='
|| ':1' || ' where ' || p_column_name || ' like ' || '''' || p_pattern || ''
';
execute immediate l_sql using l_data;
END; --Procedure
/
I'm aware that the constraint "like 'pattern'" doesn't work for LONGs and I could live with a modification such that I test for a different field in the table row for the identification of that row (and discard it later).
Changing a CLOB back into a LONG (by an ALTER TABLE MODIFY COLUMN) doesn't work either, so it's crucial to me that I find a way to fill the file content into a LONG into an already existing table.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is there a function to split a string in plsql?
I have following oracle function
create or replace
function FUN_CUSTID(t in varchar2)
RETURN VARCHAR2
IS
TYPE str_array2 IS TABLE OF VARCHAR2(100);
v_names str_array2;
v_condition varchar2(1000);
BEGIN
v_condition := '(';
select REGEXP_SUBSTR(t, '[^ ,]+', 1, rownum)
bulk collect into v_names
from DUAL
connect by level <= length (regexp_replace(t, '[^ ,]+')) + 1;
FOR indx in v_names.FIRST..v_names.LAST LOOP
if (indx = v_names.LAST) then
v_condition := v_condition || '''' || v_names(indx) ||'''';
else
v_condition := v_condition || '''' || v_names(indx) ||''',';
end if;
v_condition := v_condition || ')';
END LOOP;
return v_condition;
end FUN_CUSTID;
/
Now i want to call this function from where clause like
SELECT customer_id,
name_remark,
wbs_id,
service_circuit_id,
sum(actual_minutes) TOTAL_USAGE,
min(first_connection_time) DATE_FROM,
max(first_connection_time) DATE_TO,
sum(amount) AMOUNT
FROM temp
FROM customer_id IN (SELECT FUN_CUSTID('CUST00001,CUST00002') FROM DUAL)
GROUP BYcustomer_id, name_remark, wbs_id, service_circuit_id
ORDER BY customer_id;
When i run this function directly and replace value in place of function call in upper query its work perfect with some records but upper query could not work.
How to call function in where clause ?
Your function generates its output in v_condition as ('A','B',.....) this is one whole string for Oracle in the select query and will not work with IN. Consider using dynamic SQL for this. Oracle treats the output of the function as one singe value as ('A','B',.....) so the IN clause would be parsed as-
IN ('('A','B',.....)',...)
Which is not what you want. You want something like below
IN ('A','B',.....)
And this can be achieved by dynamic SQL.
A client wants to store anything in seperate tables. (Long story, it's nescessary).
To do this, i've build an Postgres function to create new tables on the fly in it's own namespace.
These tables can have 2, 4, or 100 columns, just what the user wants.
No problem, this works. The used datatypes in these dynamic tables are native, e.g. text, booleans, integers, etcetera.
Now comes the problem, i've got to insert data into these tables. The point is, the users can not access the tables directly, they'll do this through a function.
For a couple of datatypes is this not a problem, but for the text datatypes it's problematic.
Here is the function till now:
-- Function: add(integer, text[])
-- DROP FUNCTION add(integer, text[]);
CREATE OR REPLACE FUNCTION add(id integer, fields text[])
RETURNS integer AS
$BODY$
DECLARE
l_line_ending text := ')';
l_fieldtype integer;
l_ito_table_name text;
l_ito_fieldnames text;
l_field ito_fields%rowtype;
l_first_loop boolean := true;
l_values_to_insert text := 'VALUES (';
l_loop_counter integer := 0;
l_query text;
BEGIN
select into l_ito_table_name ito_table_name from ito where id = target_ito_id;
l_ito_fieldnames := 'insert into ' || l_ito_table_name || '(';
FOR l_field IN SELECT * FROM ito_fields
WHERE ito_fields.ito_id = target_ito_id
order by ito_fields.id asc
LOOP
l_loop_counter := l_loop_counter +1;
l_fieldtype := l_field.fieldtype;
if not l_first_loop THEN
l_values_to_insert := (l_values_to_insert || ', ');
end if;
if l_field.fieldtype = 1 THEN
l_values_to_insert := (l_values_to_insert || '''' || (fields[l_loop_counter]) || '''' );
elsif l_field.fieldtype = 2 THEN
l_values_to_insert := quote_literal(l_values_to_insert || private.cast_to_integer(fields[l_loop_counter]));
elsif l_field.fieldtype = 3 THEN
l_values_to_insert := quote_literal(l_values_to_insert || private.cast_to_boolean(fields[l_loop_counter]));
elsif l_field.fieldtype = 4 THEN
l_values_to_insert := quote_literal(l_values_to_insert || private.cast_to_float(fields[l_loop_counter]));
else
return 103;
end if;
if l_first_loop then
l_ito_fieldnames := l_ito_fieldnames || l_field.column_name;
l_first_loop := false;
else
l_ito_fieldnames := l_ito_fieldnames || ', ' || l_field.column_name;
end if;
END LOOP;
l_ito_fieldnames := l_ito_fieldnames || l_line_ending;
l_values_to_insert := ((l_values_to_insert) || (l_line_ending));
l_query := (l_ito_fieldnames || l_values_to_insert);
EXECUTE l_query;
return 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION add(integer, text[])
OWNER TO postgres;
The table ito_fields stores all the field metadata, so the datatype, versions, descriptions are stored here.
the ito table stores all the dynamic table data.
The point in this function are the quotes. The insert function is created dynamicly, therefore i've got to add some quotes around the text fields in the insert function.
Postgres is giving errors as soon as i do that. Even with the quote_literal functions it's still a problem, because of the string concatenation (i know, security risks, but that's no problem for now).
I've tried to use quote_literal, quote_ident, even replacing the quotes (') with a replacement, until the execute function (replace(query, l_quote_rep, '''').. I really don't have a clue now how to fix this...
Thanks in advance.
Arrays, aggregates, quote_ident and quote_nullable are your friends.
This should work, and code is shorter:
CREATE OR REPLACE FUNCTION add(id integer, fields text[])
RETURNS integer AS
$BODY$
DECLARE
l_ito_table_name text;
l_query text;
l_fields text;
r_values text;
BEGIN
--get table name
SELECT INTO l_ito_table_name quote_ident(ito_table_name) FROM ito WHERE id = target_ito_id;
-- get column names
SELECT INTO l_fields
array_to_string( array_agg(quote_ident(column_name)), ',' )
FROM ito_fields
WHERE ito_fields.ito_id = target_ito_id
order by ito_fields.id asc;
-- prepare values
SELECT INTO r_values
array_to_string( array_agg(quote_nullable(u.name)), ',' )
FROM unnest(fields) u(name);
l_query := 'insert into ' || l_ito_table_name || '(' || l_fields || ') values (' || r_values || ')';
EXECUTE l_query;
return 0; -- why 0?
END;
$BODY$;
I have a plpgsql function that executes a query, but when i put an array into it it wont parse it, at least it gives me this error:
[FAIL] syntax error at or near "{"
LINE 21: AND c.category_uuid != ANY({"c0857e20-111e-11e0-ac64-0800...
CONTEXT: PL/pgSQL function "get_membercategories" line 22 at FOR over EXECUTE statement
This is the complete line:
AND c.category_uuid != ANY({"c0857e20-111e-11e0-ac64-0800200c9a66"})
This is how my function looks
CREATE OR REPLACE FUNCTION get_membercategories(in_company_uuid uuid, in_parent_uuid uuid, in_start integer, in_limit integer, in_sortby character varying, in_order character varying, in_querystring character varying, IN in_excludestring UUID[], OUT out_status integer, OUT out_status_description character varying, OUT out_value character varying[]) RETURNS record
LANGUAGE plpgsql
AS $$
DECLARE
temp_record RECORD;
altered_parent_uuid UUID;
temp_out_value VARCHAR[];
temp_iterator INTEGER := 0;
temp_parent_uuidstring VARCHAR;
temp_excludestring VARCHAR := '';
BEGIN
IF in_parent_uuid IS NOT NULL THEN
temp_parent_uuidstring := 'AND c.parent_uuid = ''' || in_parent_uuid || '''';
ELSE
temp_parent_uuidstring := 'AND c.parent_uuid IS NULL';
END IF;
IF in_excludestring IS NOT NULL THEN
temp_excludestring := 'AND c.category_uuid != ANY(' || in_excludestring || ')';
END IF;
FOR temp_record IN EXECUTE '
SELECT
c.name,
c.category_uuid,
mc.password,
(
SELECT COUNT(*)
FROM
targetgroupusers tgu
WHERE
tgu.targetgroup_uuid = mc.targetgroup_uuid
) AS receivers
FROM
membercategories AS mc
LEFT JOIN
categories AS c
ON
mc.category_uuid = c.category_uuid
WHERE
c.isdeleted IS NULL
' || temp_excludestring || '
AND
mc.company_uuid = ''' || in_company_uuid || '''
' || in_querystring || '
' || temp_parent_uuidstring || '
ORDER BY
' || in_sortby || ' ' || in_order || '
OFFSET
' || in_start || '
LIMIT
' || in_limit
LOOP
temp_out_value[temp_iterator] = ARRAY[temp_record.category_uuid::VARCHAR(36), temp_record.name::CHARACTER VARYING, temp_record.receivers::CHARACTER VARYING, CASE WHEN temp_record.password IS NOT NULL THEN '1' ELSE '0' END];
temp_iterator = temp_iterator+1;
END LOOP;
out_status := 0;
out_status_description := 'Member categories successfully retrieved';
out_value := temp_out_value;
RETURN;
END$$;
I am clueless at this point, any help is very much appreciated!
Your problem is that the the array gets expanded to an invalid string (as you can see in the error message). The string representation of an array needs to be enclosed into single quotes again and then casted to the approriate array type:
The following should work:
IF in_excludestring IS NOT NULL THEN
temp_excludestring := 'AND c.category_uuid != ANY(''' || in_excludestring::varchar||'''::UUID[])';
END IF;
Note that in_excludestring is explicitely cast to a varchar to force the string representation of an array, enclosed in single quotes to satisfy the "array literal" syntax and then cast back to an UUID array.
The resulting string will look something like this:
category_uuid != ANY ( '{c0857e20-111e-11e0-ac64-0800200c9a66,7fffda0c-11c9-11e0-967c-a300aec7eb54}'::UUID[] )