how to judge declare param is null on using trigger in Oracle10g? - triggers

I have defined the following trigger:
CREATE OR REPLACE TRIGGER C_INS_TASK_INSERT
BEFORE INSERT
ON C_INS_TASK
FOR EACH ROW
DECLARE v_yearPaoIdOld VARCHAR2(255);
BEGIN
SELECT BSNUM into v_yearPaoIdOld FROM T_INS_BUSINESSINFO bs
WHERE bs.pbsnum = :new.CBSNUM AND bs.permid = :new.PERMID AND ROWNUM =1;
IF(v_yearPaoIdOld IS NOT NULL) THEN
:NEW.BSNUM := v_yearPaoIdOld;
:NEW.isstart := '1';
:NEW.isfinish := '6';
:NEW.starttime := systimestamp;
END IF;
END;
```
when I execute this code ,I get that error .And then remove the trigger, the next picture is good,so I guess error in IF(v_yearPaoIdOld IS NOT NULL) THEN,but I don't know how to do it.

Related

How do I change a trigger fucntion in mysql to postgresql trigger

I have a trigger function in MySQL written as such:
CREATE TRIGGER after_customers_insert
BEFORE INSERT
ON customers FOR EACH ROW
BEGIN
SET #created := (SELECT IFNULL(created_at, NOW()) FROM customers ORDER BY created_at
DESC LIMIT 1);
IF YEAR(#created) <> YEAR(NEw.created_at) THEN
SET NEW.Newid := 1;
ELSE
SET NEW.Newid := (SELECT IFNULL(MAX(Newid),0) + 1 FROM customers);
END IF;
SET NEW.custom_id = CONCAT('KR_',DATE_FORMAT(now(), '%y'),'_',LPAD(NEW.Newid,3,'0'));
END
I tried to convert in Postgres this:
CREATE OR REPLACE FUNCTION after_customers_insert()
RETURNS trigger AS
$BODY$
BEGIN
SET #created := (SELECT IFNULL(created_at, NOW()) FROM customers ORDER BY created_at DESC LIMIT 1);
IF YEAR(#created) <> YEAR(NEw.created_at) THEN
SET NEW.Newid := 1;
ELSE
SET NEW.Newid := (SELECT IFNULL(MAX(Newid),0) + 1 FROM customers);
END IF;
SET NEW.custom_id = CONCAT('KR_',DATE_FORMAT(now(), '%y'),'_',LPAD(NEW.Newid,3,'0'));
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
but without success. Any help please??

How do I loop through a list of columns in pg/PLSQL?

I have a table with about 3 dozen VARCHAR columns that all need the same cleanup before the record is inserted into the database (convert empty string to NULL). There are multiple apps accessing the DB, so I'd like to do this in a trigger. I could write code to check and set each column, but I'd like to loop through the columns instead. The function I wrote does not produce an error, but it also does not set empty columns to NULL.
CREATE OR REPLACE FUNCTION public.validate_flds() RETURNS trigger AS
$BODY$
DECLARE
coldata VARCHAR;
collist VARCHAR[];
BEGIN
collist := ARRAY[NEW.fld01,NEW.fld02,NEW.fld03];
FOREACH coldata IN ARRAY collist LOOP
IF coldata = '' THEN coldata := NULL; END IF;
END LOOP;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
What am I missing?
In
IF coldata = '' THEN coldata := NULL; END IF;
you alter the value of the variable while the NEW record remains unchanged. There is no easy way to do what you want in a loop. I would suggest using NULLIF().
begin
new.fld01:= nullif(new.fld01, '');
new.fld02:= nullif(new.fld02, '');
new.fld03:= nullif(new.fld03, '');
return new;
end;

Postgresql: UPDATE before INSERT function

I have problem when create function for trigger. I want to UPDATE inserted value BEFORE INSERT data to DB.
My code look like this:
CREATE OR REPLACE FUNCTION test_func()
RETURNS TRIGGER AS
$$
DECLARE cnt INTEGER;
BEGIN
cnt := COUNT(*) FROM sample_tbl WHERE id = NEW.id AND created_date = NEW.created_date;
NEW.current_order := cnt + 1; // I want to set value of sample_tbl.current_order automatically
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER test_trigger
BEFORE INSERT
ON test_tbl
FOR EACH ROW
EXECUTE PROCEDURE test_func();
I inserted data then IDE said:
control reached end of trigger procedure without RETURN
Where: PL/pgSQL function test_func()
The error says that you must return something from the Trigger ( either NEW or NULL )
There's no Trigger needed for this. A simple View using this select query will give you the required result
--create or replace view sample_view as
select t.id, t.created_date,
row_number() OVER ( partition by id,created_date order by id ) as current_order
FROM sample_tbl t;
This will exactly match the records if updated using a Trigger
CREATE OR REPLACE FUNCTION test_func()
RETURNS TRIGGER AS
$$
DECLARE cnt INTEGER;
BEGIN
select COUNT(*) INTO cnt FROM sample_tbl WHERE id = NEW.id
AND created_date = NEW.created_date;
NEW.current_order := cnt + 1;
RETURN NEW; --required
END
$$ LANGUAGE plpgsql;
Demo
Your trigger function is just missing RETURN NEW; statement:
CREATE OR REPLACE FUNCTION test_func()
RETURNS TRIGGER AS
$$
DECLARE cnt INTEGER;
BEGIN
cnt := COUNT(*) FROM sample_tbl WHERE id = NEW.id AND created_date = NEW.created_date;
NEW.current_order := cnt + 1;
RETURN NEW;
END
$$ LANGUAGE plpgsql;

Run DELETE query in trigger function

I'm trying to run a DELETE and UPDATE query inside a trigger function in PL/pgSQL.
CREATE OR REPLACE FUNCTION trg_delete_order_layer()
RETURNS trigger AS
$BODY$
DECLARE index_ INTEGER;
DECLARE a RECORD;
BEGIN
IF EXISTS (SELECT * FROM map_layers_order WHERE id_map = OLD.id_map)
THEN index_ := position FROM map_layers_order WHERE id_map = OLD.id_map AND id_layer = OLD.id_layer AND layer_type = 'layer';
ELSE index_ := 0;
END IF;
RAISE NOTICE 'max_index % % %', index_, OLD.id_map, OLD.id_layer;
EXECUTE 'DELETE FROM map_layers_order WHERE id_map = $1 AND id_layer = $2 AND layer_type = $3' USING OLD.id_map, OLD.id_layer, 'layer';
EXECUTE 'UPDATE map_layers_order SET position = position -1 WHERE id_map = $1 AND position > $2' USING OLD.id_map, index_;
VALUES (OLD.id_map, OLD.id_layer, 'layer', index_);
RETURN OLD;
END;
$BODY$
LANGUAGE plpgsql;
I don't know why i'm getting this error in the line where it runs the DELETE query:
Query has no destination for result data
Why is this error happen and how can I solve this?
Obviously if I use EXECUTE...INTO, I get a more reasonable error saying that the query has no result.

How return SELECT * in my plpgsql

i need help in my plpgsql, must return a temporary table that has dynamic columns, how can I do this?
as the name of the columns may vary, I do not know how to finish this procedure
Sorry, google translator :D
CREATE OR REPLACE FUNCTION getreport(reportid INTEGER, userId VARCHAR)
RETURNS SETOF RECORD AS
$$
DECLARE
recordResultadoFinal RECORD;
recordResultadoNomeEspecificos RECORD;
varGetSqlRelatorio VARCHAR;
varAreaId queryreports.f_area%TYPE;
varClientId queryreports.f_client%TYPE;
varTableNameTemp VARCHAR := 'temp'||userId;
varSqlAlterTable VARCHAR := '';
varSqlUpdateTemp VARCHAR := '';
varNomeColunaSpecificData VARCHAR := '';
BEGIN
SELECT f_sql,f_area,f_client INTO varGetSqlRelatorio,varAreaId,varClientId FROM queryreports WHERE f_id = reportid;
EXECUTE 'DROP TABLE IF EXISTS '||varTableNameTemp;
EXECUTE 'CREATE TEMP TABLE '||varTableNameTemp||' AS '||varGetSqlRelatorio;
EXECUTE 'CREATE INDEX processid_idx ON '||varTableNameTemp||' USING btree (processid)';
FOR recordResultadoNomeEspecificos IN EXECUTE '
SELECT DISTINCT cs.f_id as idcoluna, cs.f_name as nomecoluna, cs.f_type as tipodado
FROM clientspecifics cs
INNER JOIN clientspecificdatas csd ON (cs.f_id = csd.f_clientspecific AND csd.f_process IN (SELECT processid FROM '||varTableNameTemp||'))
ORDER BY 2
'
LOOP
varSqlAlterTable := varSqlAlterTable||' ALTER TABLE '||varTableNameTemp||' ADD COLUMN specific_'||recordResultadoNomeEspecificos.idcoluna||' varchar;';
IF (recordResultadoNomeEspecificos.tipodado = 1) THEN varNomeColunaSpecificData := 'f_text';
ELSIF (recordResultadoNomeEspecificos.tipodado = 2) THEN varNomeColunaSpecificData := 'f_name';
ELSIF (recordResultadoNomeEspecificos.tipodado = 3) THEN varNomeColunaSpecificData := 'f_date';
ELSIF (recordResultadoNomeEspecificos.tipodado = 4) THEN varNomeColunaSpecificData := 'f_value';
ELSIF (recordResultadoNomeEspecificos.tipodado = 5) THEN varNomeColunaSpecificData := 'f_text';
END IF;
varSqlUpdateTemp := varSqlUpdateTemp||' UPDATE '||varTableNameTemp||' SET specific_'||recordResultadoNomeEspecificos.idcoluna||' = csd.'||varNomeColunaSpecificData||'
FROM clientspecificdatas csd
WHERE csd.f_process = processid
AND csd.f_clientspecific = '||recordResultadoNomeEspecificos.idcoluna||';';
END LOOP;
EXECUTE varSqlAlterTable;
EXECUTE varSqlUpdateTemp;
RETURN QUERY EXECUTE 'SELECT * FROM '||varTableNameTemp;
END;
$$ LANGUAGE 'plpgsql';
You have a few options:
You can return a refcursor and then fetch from that. This only works in transactions.
You can return an xml document and then process that in your app
You can return JSON or HSTORE.
You can return setof record and specify column lists in the function declaration but this is ugly and brittle.
The problem is that PostgreSQL needs to know return types when planning a query. this means you can't have things like jagged rows or dynamic numbers of rows. These have to be wrapped in something the planner can accommodate.