Update PSQL Column with Default Date if erroneus date - sql

I am using PostgreSQL 9.3 i have a column called appointment date in a table appointment which has erroneous dates like 21117-03-04 i wish to update all rows in the column to have a default date e.g. 1900-01-01 if the value on that column is any erroneous date. I have not yet tried any solution yet. please help.

you can use below procedure
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `MAKE_ACCURATE_DATE`()
BEGIN
DECLARE VAR_ID varchar(100);
DECLARE VAR_DATE DATE;
DECLARE VAR_FINISHED INT(11) DEFAULT 0;
DECLARE DATABASE_CURSOR CURSOR FOR
SELECT DATE(DATE_COLUMN) DATE_COLUMN,ID
FROM YOUR_TABLE_NAME;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET VAR_FINISHED = 1;
OPEN DATABASE_CURSOR;
GET_NEXTRECORD: LOOP
FETCH DATABASE_CURSOR INTO VAR_DATE,VAR_ID;
IF VAR_FINISHED = 1 THEN
LEAVE GET_NEXTRECORD;
END IF;
IF VAR_DATE =NULL THEN
UPDATE YOUR_TABLE_NAME SET DATE_COLUMN='1900-01-01' WHERE ID=VAR_ID;
END IF;
END LOOP GET_NEXTRECORD;
CLOSE DATABASE_CURSOR;
END$$
DELIMITER ;
OR
FOR POSTGRES
CREATE OR REPLACE FUNCTION IS_VALID_DATE(S VARCHAR) RETURNS BOOLEAN AS $$
BEGIN
PERFORM S::DATE;
RETURN TRUE;
EXCEPTION WHEN OTHERS THEN
RETURN FALSE;
END;
$$ LANGUAGE PLPGSQL;
And make change in above function as below.
CREATE OR REPLACE FUNCTION MAKE_ACCURATE_DATE()
RETURNS void AS
$BODY$
DECLARE
RECORD RECORD;
COUNT INT;
BEGIN
COUNT=0;
FOR RECORD IN SELECT * FROM cpad.dtl_patientappointment;
LOOP
IF(!IS_VALID_DATE(RECORD.appointmentdate)) THEN
UPDATE cpad.dtl_patientappointment SET appointmentdate='1900-01-01' WHERE patientappointmentid=RECORD.patientappointmentid;
END IF;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql;
And execute this line
SELECT MAKE_ACCURATE_DATE();
It will fetch your record from table one by one and as
DATE(DATE_COLUMN) is there,it will returns null if date is not proper.So after applying condition it will update that record with specific ID.
Hope this will helps.

Related

update in a function sql in dbeaver/postgres

I have to update a field in a table with concat() and I'm thinking to use a function with an update sql. I also want to have a rollback if update doesnt' work.
I have this function but it just works the select sql and for the first row of the table "clients"
CREATE OR REPLACE FUNCTION value_concat()
RETURNS record
LANGUAGE plpgsql
AS $function$
DECLARE
rows_affected integer := 0;
query constant text not null := 'select * from db.clients';
result record;
BEGIN
EXECUTE query INTO result;
RETURN result;
UPDATE db.clients SET clients.name = concat(clients.name, '-US');
exception when raise_exception
then
begin
rows_affected := 0;
rollback;
end;
RETURN record;
END;
$function$
;
Do I have to make a select sql before the update?
Why the update is not working, should I do a for/loop before the update sql?
The below code returns just one record and not all records form the select sql, why?
EXECUTE query INTO result;
RETURN result;
I hope this help.
CREATE OR REPLACE FUNCTION value_concat()
RETURNS TABLE (
field_name1 VARCHAR,
field_name2 INT [, ...]
)
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE db.clients SET clients.name = concat(clients.name, '-US');
RETURN QUERY
select * from db.clients;
exception when raise_exception
then
begin
rows_affected := 0;
rollback;
end;
RETURN record;
END;
$function$
;
Do I have to make a select SQL before the update?
There's no need to select, it works well. You should check the query to apply where for unwanted updates for other records.
Why the update is not working, should I do a for/loop before the
update SQL?
Because you're returning before the update and the rest of the code is always skipped.
The below code returns just one record and not all records form the
select sql, why?
It's because you just return a record type. the record type can only store one row from a result.

Snowflake SQL stored procedure and save value from query with dynamic SQL

I am writing a SQL stored procedure in Snowflake (language SQL NOT Javascript).
I am trying to save a count from a SELECT statement into a variable and the table name for the select statement needs to come from a variable.
Here is what I have so far but this is failing. I don't know where to put the USING or if I can even do this? I feel like I just don't have it syntactically correct yet.
create or replace procedure myprocedure(DBNAME varchar(16777216))
returns int
language sql
as
$$
DECLARE
excludeCount int;
fullyQualifiedProceduresTable varchar(16777216);
BEGIN
fullyQualifiedProceduresTable := CONCAT(DBNAME, '.INFORMATION_SCHEMA.PROCEDURES');
excludeCount := (SELECT count(*) as count from TABLE (?) WHERE PROCEDURE_OWNER = '<ROLE NAME>') USING fullyQualifiedProceduresTable ;
IF (excludeCount > 0) THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END;
$$;
This is what I got to work using slightly different syntax for using variables.
Syntax taken from here
create or replace procedure myprocedure(DBNAME varchar(16777216))
returns int
language sql
as
$$
DECLARE
excludeCount int;
fullyQualifiedProceduresTable varchar(16777216);
BEGIN
SELECT CONCAT(:DBNAME, '.INFORMATION_SCHEMA.PROCEDURES') into :fullyQualifiedProceduresTable;
SELECT count(*) into :excludeCount from IDENTIFIER(:fullyQualifiedProceduresTable) WHERE PROCEDURE_OWNER = '<role>';
IF (excludeCount > 0) THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END;
$$;
I am not sure why you need a procedure for this. Could you not do...
set table_name='abc';
set excludeCount=(select case when count(*)>0 then 1 else 0 end from identifier($table_name));
try this
call my_proc('bus_day');
create or replace procedure my_proc(table_name varchar)
returns table(a integer)
language sql
as
$$
declare
res RESULTSET;
query varchar default 'SELECT count(*) FROM ' || :table_name ;
begin
res := (execute immediate :query);
return table (res);
end;
$$;

Postgres function to return number of rows deleted per schema

I'm trying to adapt a Postgres stored procedure into a function in order to provide some feedback to the caller.
The procedure conditionally deletes rows in specific schemas, and I'd want the function to do the same, but also return the amount of rows that were deleted for each schema.
The original stored procedure is:
create or replace procedure clear_tenants()
language plpgsql as $function$
declare
tenant text;
begin
for tenant in
select tenant_schema
from public.tenant_schema_mappings
loop
execute format($ex$
delete from %I.parent
where expiration_date_time < now()
$ex$, tenant);
end loop;
end
$function$;
My current transform into a function is:
CREATE OR REPLACE FUNCTION testfun()
RETURNS TABLE(tsname varchar, amount numeric) AS
$BODY$
declare
tenant text;
trow record;
BEGIN
for tenant in
select tenant_schema
from public.tenant_schema_mappings
loop
execute format($ex$
WITH deleted AS (
delete from %I.parent
where expiration_date_time < now()
IS TRUE RETURNING *
)
tsname := tenant;
amount := (SELECT * FROM deleted;);
return next;
$ex$, tenant);
end loop;
END
$BODY$ language plpgsql;
This is probably wrong in all kinds of ways. I'm definitely confused.
When executing this with SELECT * FROM testfun(), I get the following error:
ERROR: syntax error at or near "tsname"
LINE 7: tsname := tenant;
^
QUERY:
WITH deleted AS (
delete from anhbawys.parent
where expiration_date_time < now()
IS TRUE RETURNING *
)
tsname := tenant;
amount := (SELECT * FROM deleted;);
return next;
CONTEXT: PL/pgSQL function testfun() line 9 at EXECUTE
SQL state: 42601
So clearly I'm not properly assigning the row's columns, but I'm not sure how.
I have found this question which seemed similar, but it's bit complex for my understanding.
You can use GET DIAGNOSTICS after a DELETE statement to get the number of rows deleted:
CREATE OR REPLACE FUNCTION testfun()
RETURNS TABLE(tsname varchar, amount bigint) AS
$BODY$
declare
tenant text;
BEGIN
for tenant in
select tenant_schema
from tenant_schema_mappings
loop
execute format(
'delete from %I.parent
where expiration_date_time < now()', tenant);
tsname := tenant;
GET DIAGNOSTICS amount := ROW_COUNT;
return next;
end loop;
END
$BODY$
language plpgsql;
If the answer is as simple as your question there is a system catalog for this.
select schemaname , count(n_tup_del)
from pg_catalog.pg_stat_all_tables psat
group by schemaname
You can use get diagnostics (noddy function to get my point across)
create or replace function delfunc()
returns void
as $$
declare
affected_rows int ;
begin
delete from atable where a > 998 ;
GET DIAGNOSTICS affected_rows = ROW_COUNT;
insert into logtable values(affected_rows);
end $$
language plpgsql

multiple update pgsql using cursor

this code not error but nothing data change please help me. I want to multiple update quantity from some stock table
drop FUNCTION SP_PROSES_STOCK(noresep bigint, p_post_cd varchar);
CREATE or replace FUNCTION SP_PROSES_STOCK(noresep bigint, p_post_cd varchar)
RETURNS void
LANGUAGE plpgsql
as $function$
DECLARE cursorData refcursor;
v_item_cd varchar;
v_quantity numeric;
begin
open cursorData FOR
select A.item_cd, A.quantity from trx_medical_resep B
inner join trx_resep_data A on A.medical_resep_seqno = B.medical_resep_seqno
where B.medical_resep_seqno = noresep;
fetch next from cursorData into v_item_cd,v_quantity;
while (found)
loop
update inv_pos_item set quantity = quantity - v_quantity
where item_cd = v_item_cd and pos_cd = p_post_cd;
end loop;
close cursorData;
END
$function$
I mopdify your function to make it shorter and use FOR. Also I add RAISE NOTICE for debugging. Can you try it:
CREATE or replace FUNCTION SP_PROSES_STOCK(noresep bigint, p_post_cd varchar)
RETURNS void
LANGUAGE plpgsql
as $function$
DECLARE v_cursor record;
BEGIN
FOR v_cursor IN
SELECT A.item_cd,
A.quantity
FROM trx_medical_resep B
JOIN trx_resep_data A ON A.medical_resep_seqno = B.medical_resep_seqno
WHERE B.medical_resep_seqno = noresep
LOOP
RAISE NOTICE 'Changes for %', v_curosr;
UPDATE inv_pos_item
SET quantity = quantity - v_cursor.quantity
WHERE item_cd = v_cursor.item_cd
AND pos_cd = p_post_cd;
END LOOP;
END
$function$
After debugging you can remove RAISE NOTICE.
You don't need a loop for this. A single UPDATE statement will be a lot faster:
CREATE or replace FUNCTION SP_PROSES_STOCK(noresep bigint, p_post_cd varchar)
RETURNS void
as
$function$
begin
update inv_pos_item
set quantity = quantity - v.quantity
from (
select A.item_cd, A.quantity
from trx_medical_resep B
join trx_resep_data A on A.medical_resep_seqno = B.medical_resep_seqno
where B.medical_resep_seqno = noresep
) v
where item_cd = v.item_cd and pos_cd = p_post_cd;
END;
$function$
LANGUAGE plpgsql;
There are scenarios where a cursor is needed. For example lets say you are updating 10 million rows and an exception is thrown at the 6th million record the the update will fail and then it will rollback all of the rows that were updated prior to the failure. If a cursor is used it will be slower but you will have greater control over the process and be able to bypass the error and continue with the posting of the updates. But then again your milage may vary...

Looping through a postgres table and returning the wekid

I have a requirement to loop through a table having all the reporting periods right from 2010.
I need to loop through this table so that i when I pass the date string, it takes at and compares it if it is less than the reporting_periods_o and returns back the reporting_period_string.
CREATE OR REPLACE FUNCTION rpt.reportingperiods(IN v_date text)
RETURNS SETOF text AS
$BODY$
DECLARE
v_in_date INT := CAST(v_date AS INT);
i INT;
v_sk INT;
BEGIN
For i IN SELECT rpt_sk FROM rpt.REPORTING_PERIODS LOOP
SELECT rpt_sk INTO v_sk FROM rpt.REPORTING_PERIODS where rpt_sk = i;
IF v_in_date <= (SELECT reporting_periods_o FROM rpt.REPORTING_PERIODS
WHERE rpt_sk = i)
THEN RETURN QUERY SELECT MIN(reporting_periods)reporting_periods FROM rpt.REPORTING_PERIODS WHERE rpt_sk = v_sk AND CAST(reporting_periods AS DATE) > CAST(now() AS DATE);
END IF;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
My reporting periods has three fields:
rpt_sk, reporting_periods, reporting_periods_o
1 2014-03-08 20140308
I have implemented a function but when I execute I am getting multiple records, I need to be able to get back one record.