insert into not work in function postgresql - sql

I have the following procedure, when I run it displays all the insertions in console, the field ind_id is increased but does not save any record.
create or replace function renameDescripcionTramite() returns varchar as $$
declare
aguja record;
pajar record;
last_id integer;
begin
for aguja in
select trim(t.tra_descripcion) tra_descripcion from tab_tramite t
join tab_serietramite st on st.tra_id = t.tra_id
group by t.tra_descripcion
order by t.tra_descripcion loop
for pajar in
select u.uni_id,s.ser_id,st.sts_id,t.tra_id,trim(t.tra_descripcion) tra_descripcion
from tab_unidad u join tab_series s on u.uni_id = s.uni_id
join tab_serietramite st on st.ser_id = s.ser_id
join tab_tramite t on t.tra_id = st.tra_id
order by u.uni_id,s.ser_id,t.tra_id loop
if aguja.tra_descripcion = pajar.tra_descripcion then
insert into tab_indexpediente (uni_id,ser_id,tra_id) values (pajar.uni_id,pajar.ser_id,pajar.tra_id);-- returning ind_id into last_id;
--update tab_serietramite set wid = last_id where sts_id = pajar.sts_id;
RAISE NOTICE 'insert into tab_indexpediente (uni_id,ser_id,tra_id) values (%',cast(pajar.uni_id as text)
|| ',' || cast(pajar.ser_id as text) || ',' || cast(pajar.tra_id as text) || ')';
end if;
end loop;
--RAISE NOTICE ' ANTERIOR ES %',aguja;
end loop;
return tra_descripcion;
end;
$$ LANGUAGE plpgsql;
Can someone help me please?

Yes, I did not know that this would affect the procedure.
Thank's laurenz Albe

Related

COPY Variant into S3 from Snowflake

Attached picture is my source data in a Snowflake table. I need to copy this into S3 as 2 individual files.
EXECUTE IMMEDIATE
$$
DECLARE
load_dt_tss timestamp;
file_name varchar2(30);
c1 CURSOR FOR SELECT DISTINCT RECORD_CONTENT, LOAD_DT_TS FROM mytable where LOAD_DT_TS >= '2022-02-09 00:00:00';
BEGIN
for record in c1 do
load_dt_tss:=RECORD.LOAD_DT_TS;
file_name:=load_dt_tss||'.csv';
EXECUTE IMMEDIATE 'copy into #STG_SF_S3/'||:file_name|| 'from (select record_content from mytable where LOAD_DT_TS >= ''2022-02-09 00:00:00'') FILE_FORMAT = (TYPE=JSON,COMPRESSION = NONE) single = FALSE overwrite=FALSE';
end for;
RETURN 0;
END;
$$ ;
Above given is the Query I'm using. When running this query, I get Error as:
"Uncaught exception of type 'STATEMENT_ERROR' on line 10 at position 3 : SQL compilation error: syntax error line 1 at position 40 unexpected '21' ".
Can someone please help.
CREATE database AAP_LDS_DEV_DB;
create schema AAP_LDS_DEV_DB.PURCHASE_ORDER;
create table AAP_LDS_DEV_DB.PURCHASE_ORDER.T_PO_STG(RECORD_CONTENT variant, LOAD_DT_TS timestamp_ntz );
insert into AAP_LDS_DEV_DB.PURCHASE_ORDER.T_PO_STG (SELECT parse_json(column1), to_timestamp(column2) from values
('{"blar":"blar1"}','2022-02-10'),
('{"blar":"blar2"}','2022-02-11')
);
EXECUTE IMMEDIATE
$$
DECLARE
sql text;
file_name text;
c1 CURSOR FOR SELECT DISTINCT RECORD_CONTENT, LOAD_DT_TS FROM AAP_LDS_DEV_DB.PURCHASE_ORDER.T_PO_STG where LOAD_DT_TS >= '2022-02-09 00:00:00';
BEGIN
for record in c1 do
file_name := to_char(RECORD.LOAD_DT_TS, 'yyyymmdd_hhmmss') || '.csv';
sql := 'copy into #STG_SF_S3_DEV_JJC/' || :file_name || ' from (select record_content from AAP_LDS_DEV_DB.PURCHASE_ORDER.T_PO_STG where LOAD_DT_TS = ''' || RECORD.LOAD_DT_TS || ''') FILE_FORMAT = (TYPE=JSON,COMPRESSION = NONE) single = FALSE overwrite=FALSE';
EXECUTE IMMEDIATE sql;
end for;
RETURN 0;
END;
$$ ;
EXECUTE IMMEDIATE
$$
DECLARE
file_name varchar2(30);
records variant;
c1 CURSOR FOR SELECT DISTINCT RECORD_CONTENT, LOAD_DT_TS FROM mytable where LOAD_DT_TS >= '2022-02-09 00:00:00';
BEGIN
for record in c1 do
file_name := 'PO'||'_'||to_date(RECORD.LOAD_DT_TS)||'_'||TO_TIME(RECORD.LOAD_DT_TS)||'.json';
records := RECORD.record_content;
create or replace temporary table temp_test_pvt(records variant);
insert into temp_test_pvt select parse_json(:records) ;
EXECUTE IMMEDIATE 'copy into #STG_SF_S3/' || :file_name ||
' from (select distinct records from temp_test_pvt)
FILE_FORMAT = (TYPE=JSON, COMPRESSION = NONE) single = true overwrite=FALSE detailed_output = TRUE ';
end for;
RETURN 0;
END;
$$ ;
This is my final query. With this I am able to COPY one record as one file from Snowflake table into S3.

Execute text string returned from function as a query

I have a function with the following "signature":
CREATE OR REPLACE FUNCTION iterate_test()
RETURNS text
LANGUAGE plpgsql
...
That returns a query in text form:
SELECT DISTINCT(t1.date) AS date, AMD.adjusted_close AS AMD, GME.adjusted_close AS GME FROM clean_daily AS t1 INNER JOIN clean_daily AS AMD ON t1.date=AMD.date AND AMD.ticker='AMD' INNER JOIN clean_daily AS GME ON AMD.date=GME.date AND GME.ticker='GME' ORDER BY t1.date DESC;
How can I execute this string as a query? I have tried EXECUTE like this:
EXECUTE iterate_test();
EXECUTE QUERY iterate_test();
But I can't get it to work. How do I execute a data query in the form of text?
Full function code:
CREATE OR REPLACE FUNCTION iterate_test()
RETURNS text
LANGUAGE plpgsql
AS
$$
DECLARE
temprow record;
str_query text := 'SELECT DISTINCT(t1.date) AS date';
prev_table text := 't1';
BEGIN
FOR temprow IN SELECT * FROM portfolios WHERE user_name='snigelnmjau'
LOOP
str_query := str_query || format(', %s.adjusted_close AS %s', temprow.ticker, temprow.ticker);
END LOOP;
str_query := str_query || format(' FROM clean_daily AS t1 ');
FOR temprow IN SELECT * FROM portfolios WHERE user_name='snigelnmjau'
LOOP
str_query := str_query || format(E'INNER JOIN clean_daily AS %s ON %s.date=%s.date AND %s.ticker=''%s'' ', temprow.ticker, prev_table, temprow.ticker, temprow.ticker, temprow.ticker);
prev_table := temprow.ticker;
END LOOP;
str_query := str_query || 'ORDER BY t1.date DESC;';
--EXECUTE str_query;
RETURN str_query;
END;
$$;
To answer your question: use RETURN QUERY EXECUTE. See:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Function to loop through and select data from multiple tables
Refactor a PL/pgSQL function to return the output of various SELECT queries
Your function with a couple of fixes, including one for the core question:
CREATE OR REPLACE FUNCTION pg_temp.iterate_test()
RETURNS SETOF text -- !!
LANGUAGE plpgsql AS
$func$
DECLARE
temprow record;
str_query text := 'SELECT DISTINCT t1.date'; -- !!
prev_table text := 't1';
BEGIN
FOR temprow IN
SELECT * FROM portfolios WHERE user_name = 'snigelnmjau'
LOOP
str_query := str_query || format(', %1$I.adjusted_close AS %1$I', temprow.ticker); -- !!
END LOOP;
str_query := str_query || ' FROM clean_daily AS t1 '; -- !!
FOR temprow IN
SELECT * FROM portfolios WHERE user_name = 'snigelnmjau'
LOOP
str_query := str_query || format('JOIN clean_daily AS %1$I ON %2$I.date=%1$I.date AND %1$I.ticker=%1$L ', temprow.ticker, prev_table); -- !!
prev_table := temprow.ticker;
END LOOP;
str_query := str_query || 'ORDER BY t1.date DESC';
RETURN QUERY EXECUTE str_query; -- !!!!
END
$func$;
But the whole statement may possibly be generated in a single SELECT statement without looping (twice). And why DISTINCT?
Moreover, I am not convinced we need dynamic SQL to begin with. A plain query may be possible.

Errors in PLSQL -

Morning,
I'm trying to write a script that will convert Unload tables (UNLD to HDL files) creating a flat file using PLSQL. I keep getting syntax errors trying to run it and would appreciate some help from an expert out there!
Here are the errors:
Error(53,21): PLS-00330: invalid use of type name or subtype name
Error(57,32): PLS-00222: no function with name 'UNLDTABLE' exists in this scope
Our guess is that the unldTable variable is being treated as a String, rather than a database table object (Not really expereinced in PLSQL)
CREATE OR REPLACE PROCEDURE UNLD_TO_HDL (processComponent IN VARCHAR2)
IS
fHandle UTL_FILE.FILE_TYPE;
concatData VARCHAR2(240);
concatHDLMetaTags VARCHAR2(240);
outputFileName VARCHAR2(240);
TYPE rowArrayType IS TABLE OF VARCHAR2(240);
rowArray rowArrayType;
emptyArray rowArrayType;
valExtractArray rowArrayType;
hdlFileName VARCHAR2(240);
unldTable VARCHAR2(240);
countUNLDRows Number;
dataType VARCHAR2(240);
current_table VARCHAR2(30);
value_to_char VARCHAR2(240);
BEGIN
SELECT HDL_FILE_NAME
INTO hdlFileName
FROM GNC_HDL_CREATION_PARAMS
WHERE PROCESS_COMPONENT = processComponent;
SELECT UNLD_TABLE
INTO unldTable
FROM GNC_HDL_CREATION_PARAMS
WHERE PROCESS_COMPONENT = processComponent
FETCH NEXT 1 ROWS ONLY;
SELECT LISTAGG(HDL_META_TAG,'|')
WITHIN GROUP(ORDER BY HDL_META_TAG)
INTO concatHDLMetaTags
FROM GNC_MIG_CONTROL
WHERE HDL_COMP = processComponent;
SELECT DB_FIELD
BULK COLLECT INTO valExtractArray
FROM GNC_MIG_CONTROL
WHERE HDL_COMP = processComponent
ORDER BY HDL_META_TAG;
fHandle := UTL_FILE.FOPEN('./', hdlFileName, 'W');
UTL_FILE.PUTF(fHandle, concatHDLMetaTags + '\n');
SELECT num_rows INTO countUNLDRows FROM user_tables where table_name = unldTable;
FOR row in 1..countUNLDRows LOOP
rowArray := emptyArrayType;
FOR value in 1..valExtractArray.COUNT LOOP
rowArray.extend();
SELECT data_type INTO dataType FROM all_tab_columns where table_name = unldTable AND column_name = valExtractArray(value);
IF dataType = 'VARCHAR2' THEN (SELECT valExtractArray(value) INTO value_to_char FROM current_table WHERE ROWNUM = row);
ELSIF dataType = 'DATE' THEN (SELECT TO_CHAR(valExtractArray(value),'YYYY/MM/DD') INTO value_to_char FROM current_table WHERE ROWNUM = row);
ELSIF dataType = 'NUMBER' THEN (SELECT TO_CHAR(valExtractArray(value)) INTO value_to_char FROM current_table WHERE ROWNUM = row);
ENDIF;
rowArray(value) := value_to_char;
END LOOP;
concatData := NULL;
FOR item in 1..rowArray.COUNT LOOP
IF item = rowArray.COUNT
THEN concatData := (COALESCE(concatData,'') || rowArray(item));
ELSE concatData := (COALESCE(concatData,'') || rowArray(item) || '|');
END IF;
END LOOP;
UTL_FILE.PUTF(fHandle, concatData + '/n');
END LOOP;
UTL_FILE.FCLOSE(fHandle);
END;
Thanks,
Adam
I believe it is just an overlook in your code. You define unldTable as a varchar, which is used correctly until you try to access it as if it were a varray on line 51
rowArray(value) := unldTable(row).valExtractArray(value);
Given that you have not defined it as a varray, unldTable(row) is making the interpreter believe that you are referring to a function.
EDIT
Now that you have moved on, you should resolve the problem of invoking SELECT statements on tables that are unknown at runtime. To do so you need to make use of Dynamic SQL; you can do it in several way, the most direct being an Execute immediate statement in your case:
mystatement := 'SELECT valExtractArray(value) INTO :value_to_char FROM ' || current_table || ' WHERE ROWNUM = ' || row;
execute immediate mystatement USING OUT value_to_char;
It looks like you need to generate a cursor as
select [list of columns from GNC_MIG_CONTROL.DB_FIELD]
from [table name from GNC_HDL_CREATION_PARAMS.UNLD_TABLE]
Assuming setup like this:
create table my_table (business_date date, id integer, dummy1 varchar2(1), dummy2 varchar2(20));
create table gnc_hdl_creation_params (unld_table varchar2(30), process_component varchar2(30));
create table gnc_mig_control (db_field varchar2(30), hdl_comp varchar2(30), hdl_meta_tag integer);
insert into my_table(business_date, id, dummy1, dummy2) values (date '2018-01-01', 123, 'X','Some more text');
insert into gnc_hdl_creation_params (unld_table, process_component) values ('MY_TABLE', 'XYZ');
insert into gnc_mig_control (db_field, hdl_comp, hdl_meta_tag) values ('BUSINESS_DATE', 'XYZ', '1');
insert into gnc_mig_control (db_field, hdl_comp, hdl_meta_tag) values ('ID', 'XYZ', '2');
insert into gnc_mig_control (db_field, hdl_comp, hdl_meta_tag) values ('DUMMY1', 'XYZ', '3');
insert into gnc_mig_control (db_field, hdl_comp, hdl_meta_tag) values ('DUMMY2', 'XYZ', '4');
You could build a query like this:
select unld_table, listagg(expr, q'[||'|'||]') within group (order by hdl_meta_tag) as expr_list
from ( select t.unld_table
, case tc.data_type
when 'DATE' then 'to_char('||c.db_field||',''YYYY-MM-DD'')'
else c.db_field
end as expr
, c.hdl_meta_tag
from gnc_hdl_creation_params t
join gnc_mig_control c
on c.hdl_comp = t.process_component
left join user_tab_columns tc
on tc.table_name = t.unld_table
and tc.column_name = c.db_field
where t.process_component = 'XYZ'
)
group by unld_table;
Output:
UNLD_TABLE EXPR_LIST
----------- --------------------------------------------------------------------------------
MY_TABLE to_char(BUSINESS_DATE,'YYYY-MM-DD')||'|'||ID||'|'||DUMMY1||'|'||DUMMY2
Now if you plug that logic into a PL/SQL procedure you could have something like this:
declare
processComponent constant gnc_hdl_creation_params.process_component%type := 'XYZ';
unloadSQL long;
unloadCur sys_refcursor;
text long;
begin
select 'select ' || listagg(expr, q'[||'|'||]') within group (order by hdl_meta_tag) || ' as text from ' || unld_table
into unloadSQL
from ( select t.unld_table
, case tc.data_type
when 'DATE' then 'to_char('||c.db_field||',''YYYY/MM/DD'')'
else c.db_field
end as expr
, c.hdl_meta_tag
from gnc_hdl_creation_params t
join gnc_mig_control c
on c.hdl_comp = t.process_component
left join user_tab_columns tc
on tc.table_name = t.unld_table
and tc.column_name = c.db_field
where t.process_component = processComponent
)
group by unld_table;
open unloadCur for unloadSQL;
loop
fetch unloadCur into text;
dbms_output.put_line(text);
exit when unloadCur%notfound;
end loop;
close unloadCur;
end;
Output:
2018/01/01|123|X|Some more text
2018/01/01|123|X|Some more text
Now you just have to make that into a procedure, change dbms_output to utl_file and add your meta tags etc and you're there.
I've assumed there is only one distinct unld_table per process component. If there are more you'll need a loop to work through each one.
For a slightly more generic approach, you could build a cursor-to-csv generator which could encapsulate the datatype handling, and then you'd only need to build the SQL as select [columns] from [table]. You might then write a generic cursor to file processor, where you pass in the filename and a cursor and it does the lot.
Edit: I've updated my cursor-to-csv generator to provide file output, so you just need to pass it a cursor and the file details.

How to use string with apostrophe in function variable plpgsql

Hello I am having trouble querying when I have apostrophe in my where clause in postgresql using pgpsql function, I know that manually I could do something like:
select 'author''s'
however my word is stored in a variable, here is my function:
CREATE OR REPLACE FUNCTION public.fn_inserir_doc(caminho_arqv text, conteudo text)
RETURNS void
LANGUAGE plpgsql
AS $function$
declare
conteudo_array text array;
palavra text;
begin
execute 'insert into documento(caminho)
select ''' || caminho_arqv || '''
where not exists(select id
from documento
where caminho='''||caminho_arqv||''')';
conteudo_array := regexp_split_to_array(conteudo, E'\\s+');
FOREACH palavra in array conteudo_array
loop
if length(palavra) >=3 then
raise notice 'palavra: %', palavra;
execute 'insert into termo(descricao)
select ''' || palavra || '''
where not exists(
select id from termo
where descricao='''||palavra||''')';
execute 'insert into documento_termo(id_termo, id_documento, frequencia)
select t.id, d.id, 1
from termo t
cross join documento d
where t.descricao = '''|| palavra ||'''
and d.caminho = '''|| caminho_arqv ||'''
on conflict (id_termo, id_documento) do update set frequencia = documento_termo.frequencia + 1;';
end if;
end loop;
end;
$function$
The following sample is the one that has the problem:
select id from termo
where descricao='''||palavra||'''
because palavra contains single quote
Use dollar quoting and the function format(). Example:
create or replace function test(str text)
returns setof text language plpgsql as $$
begin
-- instead of this:
-- return query execute 'select '''||str||'''::text';
-- use:
return query execute format(
$fmt$
select %L::text
$fmt$, str);
end $$;
select * from test('O''Brian');
test
---------
O'Brian
(1 row)

Extract specific Columns from PostgreSQL table and Do an update on its values

I have a PostgreSQL database and I need to do an update over values of specific Columns. The number of columns is so big and I need to do the same operation to different table So better to extract them dynamically.
More specifically I want to extract from the table all the columns whose names ends with "_suffix" and do an update on their values.
I started trying to make a script but I don't know if it is the right road!
SELECT columns.column_name
FROM information_schema.columns
WHERE columns.table_name = 'myInitialTable' AND columns.column_name like '%\_suffix%' AND columns.table_schema = 'public';
I created a view of this query and I used it in the following function :
CREATE OR REPLACE FUNCTION updatetable() RETURNS int4 AS
$BODY$
DECLARE r RECORD;
BEGIN
FOR r IN SELECT * from v_reduced_table LOOP
update myInitialTable
set r.column_name = case
when r.column_name = '' then NULL
when r.column_name = 'value1' or r.column_name = 'value2' then 'xxxxx'
else r.column_name end;
END LOOP;
return 1;
END;
$BODY$
LANGUAGE plpgsql;
SELECT updatetable() as output;
this query do a loop on every column ending with suffix and updates its values. but when I run it I get
ERROR: syntax error at or near "$1"
LINE 1: update myInitialTable set $1 = case when $2 = '' then NULL when ...
Any help is appreciated :)
In your function you need to use dynamic commands.
The funcion format() is often very helpful.
Example data:
create table my_table(col1_suffix text, col2_suffix text, col3_suffix text);
insert into my_table values ('a', 'b', 'c');
Example function:
CREATE OR REPLACE FUNCTION update_my_table() RETURNS void AS
$BODY$
DECLARE r RECORD;
BEGIN
FOR r IN
SELECT columns.column_name
FROM information_schema.columns
WHERE columns.table_name = 'my_table'
AND columns.column_name like '%\_suffix%'
AND columns.table_schema = 'public'
LOOP
EXECUTE(FORMAT($f$
UPDATE my_table
SET %s = CASE
WHEN '%s' = 'col1_suffix' THEN 'col1'
WHEN '%s' = 'col2_suffix' OR '%s' = 'col3_suffix' THEN 'xxxxx'
END;$f$, r.column_name, r.column_name, r.column_name, r.column_name));
END LOOP;
END;
$BODY$
LANGUAGE plpgsql;
Usage:
select update_my_table();
select * from my_table;
col1_suffix | col2_suffix | col3_suffix
-------------+-------------+-------------
col1 | xxxxx | xxxxx
(1 row)