SQL Error [42601]: ERROR - Using Multiple update in dynamic sql - sql

Trying to update a table using stored proc. Getting error as
SQL Error [42601]: ERROR: query "SELECT 'update view.recon_dashboard
set dlv_count = (select count(*) from ' || $1 || ') where id='|| $2 , 'set alv_count = (select count(*) from ' || $3 || ') where id='|| $4 , CASE WHEN $5 is not null then 'set tgt_count = (select count(*) from ' || $6 || ') where id='|| $7 end" returned 3 columns
Where: PL/pgSQL function "sp_count_recon_refresh" line 7 at execute statement
1)I have a table recon_dashboard for which i loop through values of each column tgt,alv,dlv and query the db to fetch the count and update the recon_dashboard table. It is working when i update a single value in update statement like : execute 'update ca_adhoc_view.recon_dashboard set dlv_count = (select count(*) from ' || f.dlv || ') where id='|| f.id;
but it is throwing error when trying to updaate multiple columns in update statement.
create or replace procedure view.refresh_sp()
language plpgsql as
$$
declare
f record;
BEGIN
for f in select tgt,alv,dlv,id from view.recon_dashboard
loop
raise notice '% -#',f.dlv;
execute 'update view.recon_dashboard
set dlv_count = (select count(*) from ' || f.dlv || ') where id='|| f.id,
'set alv_count = (select count(*) from ' || f.alv || ') where id='|| f.id,
CASE
WHEN f.alv is not null then
'set tgt_count = (select count(*) from ' || f.tgt || ') where id='|| f.id
end;
end loop;
END;
$$;

Related

Postgresql For Loop Problems :(

I wanted to make a table that sanity checked record integrity for any duplications among my db.
I have a table currently with object names (tables) and their primary keys:
I want to create a procedure that loops through those objects with their keys, and inserts into a separate table the count of duplicates:
below is my code, but I've never done anything like this before and am new to postgres. What I have is from hours of googling/researching but every time I get closer I get a new error and am quite stuck :( Any insights would be greatly appreciated.
My newest error is I believe from my quote_ident(object_names). I don't want to query the column as postgres is reading it, I'd want that to be a raw string:
code:
do $$
declare
object_names varchar;
keys varchar;
rec record;
begin
for rec in select object_name, key from mfr_incentives.public.t_jh_dup_check
loop
object_names = rec.object_name;
keys = rec.key;
execute 'insert into mfr_incentives.public.t_jh_dup_check_final_output
select * from
(select ' || quote_ident(object_names) || ', ' || quote_ident(keys) || ', ' || ' count(*), current_date from
( select ' || keys || ', count(*)
from ' || object_names ||
' group by ' || keys || ' having count(*) > 1
) a
) a';
end loop;
end;
$$;
Found out my problem!
Being unfamiliar with the topic I finally found that I wanted quote_literal() instead of quote_ident().
The below works:
create or replace procedure public.proc_jh_dup_check()
language plpgsql
--IT WORKS NOW
as $$
declare
rec record;
begin
for rec in select object_name, key from mfr_incentives.public.t_jh_dup_check
loop
execute 'insert into mfr_incentives.public.t_jh_dup_check_final_output
select * from
(select ' || quote_literal(rec.object_name) || ', ' || quote_literal(rec.key) || ', ' || ' count(*), current_date from
( select ' || rec.key || ', count(*)
from ' || rec.object_name ||
' group by ' || rec.key || ' having count(*) > 1
) a
) a';
end loop;
end;
$$;

Count how many car/cars owner owns

I'm trying to learn plsql and got stuck in understanding some basic stuff. Here is a challenge that I'm trying to solve. I have two tables. One holds information about owners and the other is information about cars.
I want to to write an anonymous block that joins these two tables and with a for loop based on amount of cars that is registered to each owner prints how many cars each person own. furthermore I want an if statement which distinguishes between 1 Car (singular) and 2, 3 Cars (plural).
the tables are these:
CREATE TABLE owners(
id_nr VARCHAR2(13) PRIMARY KEY,
f_name VARCHAR2(20),
s_name VARCHAR2(20)
);
CREATE TABLE cars(
reg_nr VARCHAR2(6) PRIMARY KEY,
id_nr REFERENCES owners(pnr),
model VARCHAR2(20),
year NUMBER(4),
date DATE
);
The result may look like something like this:
19380321-7799, Hans, Anderson, Owns: 1 car
19490321-7899, Mike, Erikson, Owns: 2 cars
. . . etc
I know the this question was already answered but when I try following:
declare
v_suffix varchar2(1);
begin
for o in (select bilägare_pnr, fnamn, enamn,
(select count(1) from fordon where fordon_pnr = bilägare_pnr) as bilar_ägda
from bilägare)
loop
if o.pnr_fordon = 1
then v_suffix = 'bil'
else v_suffix = 'bilar'
end if;
dbms_output.put_line(o.pnr || ', ' || o.fnamn || ', ' || o.enamn
|| ' Äger: ' || o.pnr_fordon || ' bil' || v_suffix);
end loop;
end;
/
I get:
ORA-06550: line 9, column 27:
PLS-00103: Encountered the symbol "=" when expecting one of the following:
:= . ( # % ;
any tips? Im not sure how to declare v_suffix
EDIT (copied from comment on answer below):
Updating my code:
declare
cursor c_BILÄGARE is
select fnamn,enamn,pnr
from BILÄGARE;
begin
for rec in c_BILÄGARE loop
if (rec.antal>1) then
dbms_output.put_line (rec.pnr||','|| rec.fnamn || ',' ||
rec.enamn || ',' || rec.antal || 'bilar');
else
dbms_output.put_line (rec.pnr||','|| rec.fnamn || ',' ||
rec.enamn || ',' || rec.antal || 'bil');
end if;
end loop;
end;
getting:
ORA-06550: line 9, column 9: PLS-00302: component 'ANTAL' must be declared (antal=Quantity)
As noted above, you need to use := as the assignment operator. You also need a semi-colon after each statement - thus, you should use
then v_suffix := 'bil';
instead of
then v_suffix := 'bil'
So your code should look like:
declare
v_suffix varchar2(1);
begin
for o in (select bilägare_pnr, fnamn, enamn,
(select count(1)
from fordon
where fordon_pnr = bilägare_pnr) as bilar_ägda
from bilägare)
loop
if o.pnr_fordon = 1
then v_suffix := 'bil';
else v_suffix := 'bilar';
end if;
dbms_output.put_line(o.pnr || ', ' || o.fnamn || ', ' || o.enamn
|| ' Äger: ' || o.pnr_fordon || ' bil' || v_suffix);
end loop;
end;
Please try this piece of code:
begin
for rec in (select
o.id_nr || ',' || o.f_name || ',' || o.s_name || ', Owns: ' || coalesce(car.cnt, 0) || ' ' || decode(car.cnt , 1, 'Car', 'Cars') as res
from owners o
left outer join (select id_nr, count(1) as cnt from cars group by id_nr) car on (car.id_nr = o.id_nr)) loop
dbms_output.put_line(rec.res);
end loop;
end;
Thanks.
There are multiple issues in your code which is highlighted and fixed in following code:
declare
v_suffix varchar2(5); -- data length should be 5 or more
begin
for o in (select bilägare_pnr, fnamn, enamn,
(select count(1) from fordon where fordon_pnr = bilägare_pnr) as bilar_ägda
from bilägare)
loop
if o.bilar_ägda <= 1 -- replaced it from pnr_fordon and <= is used for 0 or 1 car
then v_suffix := 'bil'; -- := and ; is used here and in next statement
else v_suffix := 'bilar';
end if;
dbms_output.put_line(o.pnr || ', ' || o.fnamn || ', ' || o.enamn
|| ' Äger: ' || o.pnr_fordon || ' bil' || v_suffix);
end loop;
end;
/

How to declare a number variable where I can save th count of table in my loop

I work wirh oracle Database. I have a plsql code where i run a query in a loop for multiple tables. so, table name is a variable in my code. I would like to have another variable (a single number) that I can call inside the loop and every time it counts the total rows of each table for me
declare
Cursor C_TABLE is
select trim(table_name) as table_name
from all_tables
where table_name in ('T1', 'T2', 'T3');
V_ROWNUM number;
begin
for m in C_TABLE
loop
for i in ( select column_name
from (
select c.column_name
from all_tab_columns c
where c.table_name = m.table_name
and c.owner = 'owner1'
)
)
loop
--I have this:
execute immediate ' insert into MY-table value (select ' || i.column_name || ' from ' || m.table_name || ')';
--I want this but it does not work of course:
V_ROWNUM := execute immediate 'select count(*) from ' || m.table_name;
execute immediate ' insert into MY-table value (select ' || i.column_name || ', ' || V_ROWNUM || ' from ' || m.table_name || ')';
end loop;
end loop;
end;
/
I count not use the "insert into" because I am not selecting from 1 table but the table I want to select from changes every round.
There are three things wrong with your dynamic SQL.
EXECUTE IMMEDIATE is not a function: the proper syntax is execute immediate '<<query>>' into <<variable>>.
An INSERT statement takes a VALUES clause or a SELECT but not both. SELECT would be very wrong in this case. Also note that it's VALUES not VALUE.
COLUMN_NAME is a string literal in the dynamic SQL so it needs to be in quotes. But because the SQL statement is itself a string, quotes in dynamic strings need to be escaped so it should be `'''||column_name||'''.
So the corrected version will look something like this
declare
Cursor C_TABLE is
select trim(table_name) as table_name
from all_tables
where table_name in ('T1', 'T2', 'T3');
V_ROWNUM number;
begin
for m in C_TABLE
loop
for i in ( select column_name
from (
select c.column_name
from all_tab_columns c
where c.table_name = m.table_name
and c.owner = 'owner1'
)
)
loop
execute immediate 'select count(*) from ' || m.table_name into V_ROWNUM;
execute immediate 'insert into MY_table values ( ''' || i.column_name || ''', ' || V_ROWNUM || ')';
end loop;
end loop;
end;
/
Dynamic SQL is hard because it turns compilation errors into runtime errors. It is good practice to write the statements first as static SQL. Once you have got the basic syntax right you can convert it into dynamic SQL.
you can't assign the result of execute immediate to a variable. it is not a function.
but you can do it by using the into_clause e.g.
execute immediate 'select count(*) from ' || m.table_name into V_ROWNUM ;

Postgresql - Dynamically create multi-level ordering with function

I am creating a function which will build and execute a dynamic sql statement. Basically it is building the ORDER BY clause, which will have the ability to ORDER multiple levels deep based on an array of VARCHARs passed in by the user.
For instance a user's input would be an array:
{{orderColumn, orderDirection},{orderColumn, orderDirection},...}
Obviously, I understand where the issue is in the statement, because postgres prints it out rather clearly. The problem is that I can't figure out the correct syntax in this particular case. Perhaps I am doing it incorrectly and there is a much better route for me to take, I am not set on this approach. If I can do this without dynamic sql it would be even better. Help or insight would be appreciated.
For reference I am using PostgreSQL 9.6.9
My code so far...
CREATE OR REPLACE FUNCTION profile.sp_rider_search(
_limit integer,
_offset integer,
_term text,
_orderBy VARCHAR[],
_registration_date timestamp with time zone DEFAULT
to_timestamp((0)::double precision))
RETURNS TABLE(customer_id integer
, first_name character varying
, last_name character varying
, dob date
, cell_phone character varying
, email character varying
, registration_date timestamp with time zone
, full_count integer)
AS $func$
DECLARE
sql TEXT := 'SELECT pp.user_id AS customer_id,
pp.first_name,
pp.last_name,
pp.date_of_birth AS dob,
pp.cell_phone,
aau.email,
pp.created AS registration_date,
count(*) OVER()::integer AS full_count
FROM profile.profile pp
LEFT JOIN authsvc.all_users aau ON aau.id = pp.user_id
WHERE pp.created > $5 AND (
pp.last_name ILIKE % || $3 || %
OR pp.first_name ILIKE % || $3 || %
OR pp.cell_phone ILIKE % || $3 || %
OR CAST(pp.user_id AS TEXT) ILIKE % || $3 || %
OR aau.email ILIKE % || $3 || %
OR CONCAT(pp.first_name,'' '',pp.last_name) ILIKE % || $3 || %
)
ORDER BY';
_item TEXT;
BEGIN
FOREACH _item IN ARRAY $4
LOOP
CASE WHEN _item[2] = 1 THEN
CASE WHEN _item[1] = 'last_name' THEN
sql := sql || ' pp.last_name, ';
WHEN _item[1] = 'first_name' THEN
sql := sql || ' pp.first_name, ';
WHEN _item[1] = 'email' THEN
sql := sql || ' aau.email, ';
WHEN _item[1] = 'phone' THEN
sql := sql || ' pp.cell_phone, ';
WHEN _item[1] = 'customer_id' THEN
sql := sql || ' pp.user_id::TEXT, ';
WHEN _item[1] = 'registration_date' THEN
sql := sql || ' pp.created::TEXT, ';
END
END
CASE WHEN _item[2] = -1 THEN
CASE WHEN _item[1] = 'last_name' THEN
sql := sql || ' pp.last_name DESC, ';
WHEN _item[1] = 'first_name' THEN
sql := sql || ' pp.first_name DESC, ';
WHEN _item[1] = 'email' THEN
sql := sql || ' aau.email DESC, ';
WHEN _item[1] = 'phone' THEN
sql := sql || ' pp.cell_phone DESC, ';
WHEN _item[1] = 'customer_id' THEN
sql := sql || ' pp.user_id::TEXT DESC, ';
WHEN _item[1] = 'registration_date' THEN
sql := sql || ' pp.created::TEXT DESC, ';
END
END
END LOOP;
sql := sql || 'LIMIT $1
OFFSET $2;'
RETURN QUERY EXECUTE sql
USING _limit,
_offset,
_term,
_orderBy,
_registration_date;
END;
$func$ LANGUAGE plpgsql;
On execution of this statement I get the error:
ERROR: syntax error at or near "END"
LINE 57: END
^
SQL state: 42601
Character: 998
I had a few syntax errors in my statement that I finally ironed out, hopefully this will help others. I needed to add in END CASE; for each case statement, I also had to add a semicolon after OFFSET $2; as well as remove a trailing comma from the order by statements.
Final working code -->
CREATE OR REPLACE FUNCTION profile.sp_rider_search(
_limit integer,
_offset integer,
_term text,
_orderBy VARCHAR[],
_registration_date timestamp with time zone DEFAULT
to_timestamp((0)::double precision),
_preferred_column TEXT DEFAULT '')
RETURNS TABLE(customer_id integer
, first_name character varying
, last_name character varying
, dob date
, cell_phone character varying
, email character varying
, registration_date timestamp with time zone
, full_count integer)
AS $func$
DECLARE
sql TEXT := 'SELECT pp.user_id AS customer_id,
pp.first_name,
pp.last_name,
pp.date_of_birth AS dob,
pp.cell_phone,
aau.email,
pp.created AS registration_date,
count(*) OVER()::integer AS full_count
FROM profile.profile pp
LEFT JOIN authsvc.all_users aau ON aau.id = pp.user_id
WHERE pp.created > $5 AND (
pp.last_name ILIKE % || $3 || %
OR pp.first_name ILIKE % || $3 || %
OR pp.cell_phone ILIKE % || $3 || %
OR CAST(pp.user_id AS TEXT) ILIKE % || $3 || %
OR aau.email ILIKE % || $3 || %
OR CONCAT(pp.first_name,'' '',pp.last_name) ILIKE % || $3 || %
)
ORDER BY';
_item TEXT;
BEGIN
FOREACH _item IN ARRAY $4
LOOP
CASE WHEN _item[2] = 1 THEN
CASE WHEN _item[1] = 'last_name' THEN
sql := sql || ' pp.last_name, ';
WHEN _item[1] = 'first_name' THEN
sql := sql || 'pp.first_name, ';
WHEN _item[1] = 'email' THEN
sql := sql || 'aau.email, ';
WHEN _item[1] = 'phone' THEN
sql := sql || 'pp.cell_phone, ';
WHEN _item[1] = 'customer_id' THEN
sql := sql || 'pp.user_id::TEXT, ';
WHEN _item[1] = 'registration_date' THEN
sql := sql || 'pp.created::TEXT, ';
END CASE;
WHEN _item[2] = -1 THEN
CASE WHEN _item[1] = 'last_name' THEN
sql := sql || ' pp.last_name DESC, ';
WHEN _item[1] = 'first_name' THEN
sql := sql || 'pp.first_name DESC, ';
WHEN _item[1] = 'email' THEN
sql := sql || 'aau.email DESC, ';
WHEN _item[1] = 'phone' THEN
sql := sql || 'pp.cell_phone DESC, ';
WHEN _item[1] = 'customer_id' THEN
sql := sql || 'pp.user_id::TEXT DESC, ';
WHEN _item[1] = 'registration_date' THEN
sql := sql || 'pp.created::TEXT DESC, ';
END CASE;
END CASE;
END LOOP;
sql := TRIM(TRAILING ',' FROM sql);
sql := sql || 'LIMIT $1
OFFSET $2;';
RETURN QUERY EXECUTE sql
USING _limit,
_offset,
_term,
_orderBy,
_registration_date;
END;
$func$ LANGUAGE plpgsql;

Oracle: how to run this query (generated column names)

I need to run a query on generated generated column names.
Here's the query:
select 'col_'||4 from MY_TABLE
Note:
"4" is a variable that is passed to this query from within the Java code
MY_TABLE is a table that contain columns with names (col_4, col_5, etc..)
Inside Oracle you need use dynamic SQL. (YourVariable value is 4 for your example)
EXECUTE IMMEDIATE ' select col_' || YourVariable || ' from MY_TABLE ';
From Java you can build any SQL and execute them
To run a dynamic SELECT statement, you have two choices:
For single row selects, you use EXECUTE IMMEDIATE ... INTO:
EXECUTE IMMEDIATE 'select col_' || l_num || ' from MY_TABLE WHERE id = 37' INTO l_result;
For selecting multiple rows, you can use a dynamic cursor:
DECLARE
TYPE MyCurType IS REF CURSOR;
my_cv MyCurType;
BEGIN
OPEN emp_cv FOR 'select col_' || l_num || ' from MY_TABLE';
...
END;
This code generates a SELECT that returns the tables with their column name:
SELECT
'SELECT ' ||(
SELECT
LISTAGG(
c.TABLE_NAME || '.' || c.COLUMN_NAME || ' AS "' || c.TABLE_NAME || '.' || c.COLUMN_NAME || '"',
', '
) WITHIN GROUP(
ORDER BY
c.TABLE_NAME
) "TABLE_NAMES"
FROM
USER_TAB_COLS c
WHERE
TABLE_NAME IN(
'PESSOA',
'PESSOA_FISICA',
'PESSOA_JURIDICA'
)
)|| 'FROM PERSON;'
FROM
DUAL;