In Snowflake I'm attempting to run a while loop where there is a start date, and the code will loop until it equals today's date.
I'm fairly new to snowflake itself.
First idea was in the while loop check if the variable date was less than the current date. I couldn't seem to get this working even with the Current_Date on it's own or attributed to a variable
execute immediate $$
declare
opdate := '2022-04-29';
currdate := Current_Date;
begin
while (opdate <=currdate) do
opdate :=dateadd(day,1,opdate);
end while;
end;
$$
;
'CURRDATE' cannot have its type inferred from initializer
The second option was to use a datediff to see if the difference between the variable and current_date was equal to zero
execute immediate $$
declare
opdate := '2022-04-29';
currndate := 1;
begin
while (currndate<=0) do
currndate := datediff(day, opdate, CURRENT_DATE);
opdate :=dateadd(day,1,opdate);
end while;
end;
$$
;
error line 7 at position 39 invalid identifier 'CURRENT_DATE'
I've tried many different variants of this code, even use for each and if loops with no luck. I'm seemingly unable to check if a variable is less than the current date. Any help/pointers would be much appreciated.
Without having Current_Date, and having a set start and end date, it appears to run fine
execute immediate $$
declare
opdate := '2022-04-29';
currdate := '2022-05-03';
begin
while (opdate <=currdate) do
opdate :=dateadd(day,1,opdate);
end while;
end;
$$
;
Can you try CURRENT_DATE() with the open and close parens? see block below
execute immediate $$
declare
opdate := '2022-04-29';
currdate := Current_Date();
begin
while (opdate <=currdate) do
opdate :=dateadd(day,1,opdate);
end while;
return opdate; --added this return
end;
$$
;
Related
I have created following stored procedure to update recon_dashboard table.
create or replace procedure ca_adhoc_view.sp_count_recon_refresh()
language plpgsql as
$$
declare
f record;
BEGIN
for f in select alvid from ca_adhoc_view.recon_dashboard
loop
raise notice '% alv',f.alv;
execute 'update ca_adhoc_view.recon_dashboard set alv_count =
(select count(*) from ' || f.alv || ')
where id='|| f.id;
end loop;
END;
$$;
It works fine unless there is a null value in the alv, then it throws an error.
I tried to check for null like:
create or replace procedure ca_adhoc_view.sp_count_recon_refresh()
language plpgsql as
$$
declare
f record;
BEGIN
for f in select alv,id from ca_adhoc_view.recon_dashboard
loop
raise notice '% alv',f.alv;
execute 'update ca_adhoc_view.recon_dashboard set alv_count =
(IF '||f.alv||' is not null
then (select count(*) from ' || f.alv || ')
END IF;)
where id='|| f.id;
end loop;
END;
$$;
But it throws error when I try to call the SP.
Error: SQL Error [42601]: ERROR: syntax error at or near "ca_view"
Where: PL/pgSQL function "sp_count_recon_refresh" line 7 at execute statement
In error "ca_view" is a recod. alv column has values like 'ca_view.table1', 'ca_view.table2' and so on.
This probably fixes your problems:
CREATE OR REPLACE PROCEDURE ca_adhoc_view.sp_count_recon_refresh()
LANGUAGE plpgsql AS
$func$
DECLARE
f record;
BEGIN
FOR f IN
SELECT alv, id FROM ca_adhoc_view.recon_dashboard
LOOP
RAISE NOTICE '% alv', f.alv;
IF f.alv IS NOT NULL THEN
EXECUTE format(
'UPDATE ca_adhoc_view.recon_dashboard
SET alv_count = (SELECT count(*) FROM %I)
WHERE id = $1', f.alv)
USING f.id;
END IF;
END LOOP;
END
$func$;
"Problems" (plural). Besides defending against a NULL value for the table name with proper syntax, this also prevents SQL injection. Your original is wide open there. Related:
PostgreSQL - Writing dynamic sql in stored procedure that returns a result set
Table name as a PostgreSQL function parameter
That said, it would be more efficient to run a single UPDATE instead of updating one row per loop iteration. Consider building and executing a single statement.
I have a request to add data that triggers a trigger that checks one condition, and only after that adds it to the purchase table. He also swears at the lines of initializations total_rasr and id_buyer invalid number, although there are the same types in the table and in the trigger. And the biggest question is, this trigger worked, but at one point it stopped and gives these errors.
INSERT INTO PAYMENT (ID, ADDRESS, DATE_PAYMENT, PAYMENT_METHOD_ID, EMPLOYEE_ID, BUYER_ID)
VALUES (Key('PAYMENT'), 'Moscow', TO_DATE('2002-08-23', 'YYYY-MM-DD'),'005!00002','1','002!00005');
trigger
create or replace TRIGGER checking_the_availability_work
BEFORE INSERT
ON PAYMENT
FOR EACH ROW
DECLARE
rasr_is_possible boolean := true;
total_rasr VARCHAR(10);
id_buyer VARCHAR(10);
AVAILABILITY_OF_WORK VARCHAR(5);
xSQL VARCHAR(100);
BEGIN
total_rasr := :NEW.PAYMENT_METHOD_ID;
id_buyer := :NEW.BUYER_ID;
IF total_rasr = '005!00002' THEN
xSQL := 'select AVAILABILITY_OF_WORK FROM BUYER WHERE ID =' || id_buyer;
execute immediate xSQL into AVAILABILITY_OF_WORK;
IF AVAILABILITY_OF_WORK = 'Нет' THEN
rasr_is_possible := false;
END IF;
END IF;
if not rasr_is_possible THEN
Raise_application_error(-20201, 'У вас нет места работы!');
end if;
END;
Why use dynamic SQL here at all? It seems really convoluted and is resulting in an error, perhaps when :new.buyer_id is null or has some other unexpected value? Try something like this, and perhaps consider a check on :new.buyer_id to make sure it has a value as expected.
DECLARE
L_AVAILABILITY_OF_WORK VARCHAR(5);
BEGIN
IF :NEW.PAYMENT_METHOD_ID = '005!00002' THEN
select AVAILABILITY_OF_WORK into L_AVAILABILITY_OF_WORK
FROM BUYER WHERE ID = :NEW.BUYER_ID;
IF L_AVAILABILITY_OF_WORK = 'Нет' THEN
Raise_application_error(-20201, 'У вас нет места работы!');
END IF;
END IF;
END;
create or replace function f() RETURNS void AS
declare
tableArray text[] := '{"ADDRESS","CONSISTENCY_CHECK","DEPARTMENT_SUPERVISION"}';
tableName CHARACTER VARYING;
value INTEGER ;
BEGIN
FOREACH tableName IN ARRAY tableArray
LOOP
select user_id from tableName where user_id=2631;
if found then
update tableName set user_id=2651 where user_id=2631;
delete from tableName where user_id=2631;
END loop;
end;
here is the error that I get when trying to execute the pgplsql: ERROR syntax error at or near "loop"
There are more issues:
the body of function should be string - you can use apostrophes or more usual and practical $$ custom string separators.
the result of SELECT should not be lost. The clause INTO is missing.
table name should not be a variable - variable cannot be used as table name. In this case you need dynamic SQL - EXECUTE statement.
camel notation for variable names should not be used for SQL language, that is case insensitive
CREATE OR REPLACE FUNCTION f()
RETURNS void AS $$
DECLARE
table_array text[] := '{"ADDRESS","CONSISTENCY_CHECK","DEPARTMENT_SUPERVISION"}';
table_name text;
value integer ;
rc integer
BEGIN
FOREACH table_name IN ARRAY table_array
LOOP
EXECUTE format('SELECT * FROM %I WHERE user_id = $1', table_name) USING 2631;
-- variable FOUND cannot be used for dynamic SQL
GET DIAGNOSTICS rc = ROW_COUNT;
IF rc > 0 THEN
EXECUTE format('UPDATE %I SET user_id = $1 WHERE user_id = $2', table_name) USING 2651, 2631;
EXECUTE format('DELETE %I WHERE user_id = $1', table_name) USING 2631;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
I have a function called GET_CLIENT_IN_SED(return sys_refcursor), it gives me a list of id numbers(single column). Now, in a procedure, I am trying to loop through each (one by one) of that values and use it for calling a second procedure (it needs a client id parameter).
PROCEDURE GET_ORDINARY_CLIENT;
PROCEDURE GET_ORDINARY_CLIENT_BY_SED
( sed_in IN varchar2, client_sed OUT SYS_REFCURSOR )
IS
ordinary_clients sys_refcursor;
BEGIN
ordinary_clients := GET_CLIENT_IN_SED(sed_in);
for item in ordinary_clients loop
client_sed := client_sed + ordinary_clients(i);
end loop;
END;
As far i could understand you need to do something like :
Function:
This function would take input as number and return a refcursor. Similar to your requirement.
CREATE OR REPLACE FUNCTION get_num_sysrefcur (num IN NUMBER)
RETURN SYS_REFCURSOR
AS
my_cursor SYS_REFCURSOR;
BEGIN
--
OPEN my_cursor FOR
WITH ntable
AS (SELECT 1 ID, 111 AGT, 'ABC' DESCRIP FROM DUAL
UNION ALL
SELECT 2 ID, 222 AGT, 'ABC' DESCRIP FROM DUAL
UNION ALL
SELECT 1 ID, 333 AGT, 'ABC' DESCRIP FROM DUAL)
SELECT AGT FROM ntable WHERE ID = num;
RETURN my_cursor;
END;
/
Block ( In your case Procedure )
-- This anonymous block will loop through the records return from the sys_refcursor. Similiar to you need where you want the second procedure to use the value of sys_refcursor and loop it(You can create procedure in place of this anonymous block).
DECLARE
a NUMBER := 1;
TYPE ta IS TABLE OF NUMBER
INDEX BY PLS_INTEGER;
b ta;
x SYS_REFCURSOR;
BEGIN
x := get_num_sysrefcur (a);
fetch x bulk collect into b;
for i in 1..b.count
loop
-- Displaying the result of the ref_cursor.
DBMS_OUTPUT.put_line (b(i));
end loop;
END;
To loop through a ref cursor is not like looping through an array or table which explains why your FOR...LOOP is not working.
In short, instead of a collection, the ref_cursor is more of a "pointer" or an "iterator" over a collection. In this other question you will find a quite clear example of iterating through a ref_cursor using FETCH.
How to use record to loop a ref cursor?
An example with your data would look like this :
PROCEDURE GET_ORDINARY_CLIENT_BY_SED(sed_in IN VARCHAR2,
client_sed OUT SYS_REFCURSOR) IS
ordinary_clients SYS_REFCURSOR;
clt NUMBER; -- assuming your cursor contains strictly numbers
BEGIN
ordinary_clients := GET_CLIENT_IN_SED(sed_in);
LOOP
FETCH ordinary_clients
INTO clt;
EXIT WHEN ordinary_clients%NOTFOUND;
dbms_output.put_line(clt);
-- do some other things here with your number
END LOOP;
END;
I have a stored procedure writen in T-SQL and I want to make it for PostgreSQL but I'm not so familiar with PostgreSQL.
My stored procedure look like this:
CREATE PROCEDURE usp_insert_allocated_time
#fld_project_id INT,
#fld_allocated_time INT
AS
DECLARE #project int;
SET #project = #fld_project_id;
DECLARE #allocated int;
DECLARE #time int;
BEGIN
SET #time = (SELECT SUM(fld_allocated_time)
FROM dbo.tbl_project_timesheet
WHERE fld_project_id =#project)
SET #allocated = (SELECT fld_allocated_days FROM dbo.tbl_project where fld_id = #project);
IF #allocated > #time
BEGIN
INSERT into dbo.tbl_project_timesheet(fld_project_id,fld_allocated_time)
VALUES(#fld_project_id,#fld_allocated_time);
END
ELSE
PRINT 'Not OK';
END
And I have to do something like this, but on line 10 I get this error:
ERROR: invalid input syntax for integer: "293.00"
SQL state: 22P02
Context: PL/pgSQL function
"SA_PRJ".usp_add_timesheet_record_new(integer,integer,numeric,numeric,character varying,character varying) line 10 at assignment
CREATE OR REPLACE FUNCTION "SA_PRJ".usp_add_timesheet_record_new(p_uid integer, p_project_id integer, p_allocated_time numeric, p_achieved_time numeric, p_task_desc character varying, p_obs character varying)
RETURNS void AS
$BODY$
declare alloc_id integer;
declare project integer;
declare allocated integer;
declare allocated_time integer;
BEGIN
project := p_project_id;
allocated_time := (SELECT SUM(fld_allocated_time)
FROM "SD_PRJ".tbl_project_timesheet
WHERE fld_project_id = project);
allocated := (SELECT fld_allocated_days FROM "SD_PRJ".tbl_project where fld_id = project);
if not "SA_ADM".usp_check_permission(p_uid, 'SA_PRJ', 'usp_add_timesheet_record') then
raise exception 'User ID % no have the permission!', p_uid;
end if;
select fld_id into alloc_id from "SD_PRJ".tbl_project_allocation where fld_emp_id = p_uid and fld_project_id = p_project_id;
BEGIN
IF (allocated > allocated_time) THEN
INSERT INTO "SD_PRJ".tbl_project_timesheet(fld_emp_id, fld_project_id, fld_is_allocated,fld_allocated_time, fld_achieved_time, fld_task_desc, fld_obs)
VALUES (p_uid,p_project_id,coalesce(alloc_id,0), p_allocated_time, p_achieved_time,p_task_desc, p_obs);
ELSE
RAISE NOTICE 'Not OK!!';
END IF;
END;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
It's more complex version in PostgreSQL for what I want.
You don't really give enough information to try and fix your problem, but the error message is pretty descriptive. You are trying to put 293.00 into an integer. Here I can reproduce:
DO
$$
DECLARE
i INT;
BEGIN
i := 293.00;
RAISE NOTICE 'i=%', i;
END
$$;
This will raise:
ERROR: invalid input syntax for integer: "293.00"
SQL state: 22P02
Context: PL/pgSQL function inline_code_block line 6 at assignment
You need to change your variable to the same datatype as the data you are trying to assign to it. For example:
DO
$$
DECLARE
i NUMERIC(5, 2);
BEGIN
i := 293.00;
RAISE NOTICE 'i=%', i;
END
$$;
This works and outputs:
NOTICE: i=293.00
Query returned successfully with no result in 14 ms.