patternMinor
Execute dynamic INSERT ON CONFLICT DO UPDATE in plpgsql function
Viewed 0 times
updateinsertfunctiondynamicplpgsqlconflictexecute
Problem
I have this table that is created every month. ex.
Example values:
I have created a function to get data from table
Example values:
Function:
```
CREATE OR REPLACE FUNCTION insert_data_func()
RETURNS TRIGGER AS
$BODY$
EXECUTE 'INSERT INTO tb_moldsummary '
|| 'SELECT NEW.machine, NEW.model, split_part(NEW.lot, ''_'', 1), '
|| 'SUBSTRING(NEW.lot FROM (POSITION(''_'' in NEW.lot)+1) FOR LENGTH(NEW.lot)), COUNT(lot), NEW.datetimestamp '
|| 'FROM model' || to_char(CURRENT_TIMESTAMP, 'YYYYMM')
|| ' WHERE lot = NEW.lot AND machine = NEW.machine GROUP BY machine, model, lot '
|| 'ON CON
model201707 ('model' = fixed; '201707' = year & month based on the current date):| COLUMN_NAME | TYPE |
|---------------|-------------|
| serial | varchar |
| lot | varchar |
| model | varchar |
| line | varchar |
| machine | varchar |
| datetimestamp | timestamptz |Example values:
| serial | lot | model | line | machine | datetimestamp |
|--------|------------|--------|------|---------|---------------------|
| 1 | 16_F22_F23 | sample | A | 1 | 2017-02-28 07:22:39 |
| 2 | 16_F22_F23 | sample | A | 1 | 2017-02-28 07:22:42 |
| 3 | 16_F22_F23 | sample | A | 1 | 2017-02-28 07:22:45 |I have created a function to get data from table
model201707 and insert it into table tb_moldsummary:| COLUMN_NAME | TYPE | | | |
|---------------|-----------|----|----|-----|
| machine_name | varchar | NN | PK | |
| model | varchar | NN | PK | UNQ |
| mold | varchar | NN | PK | UNQ |
| cavity | varchar | NN | PK | UNQ |
| totalshots | int4 | | | |
| datetimestamp | timestamp | | | |Example values:
| machine | model | m_id | cav_id | totalshots | datetimestamp |
|---------|--------|------|---------|------------|---------------------|
| 1 | sample | 16 | F22_F23 | 3 | 2017-02-28 07:22:45 |Function:
```
CREATE OR REPLACE FUNCTION insert_data_func()
RETURNS TRIGGER AS
$BODY$
EXECUTE 'INSERT INTO tb_moldsummary '
|| 'SELECT NEW.machine, NEW.model, split_part(NEW.lot, ''_'', 1), '
|| 'SUBSTRING(NEW.lot FROM (POSITION(''_'' in NEW.lot)+1) FOR LENGTH(NEW.lot)), COUNT(lot), NEW.datetimestamp '
|| 'FROM model' || to_char(CURRENT_TIMESTAMP, 'YYYYMM')
|| ' WHERE lot = NEW.lot AND machine = NEW.machine GROUP BY machine, model, lot '
|| 'ON CON
Solution
Multiple problems. This should work:
I marked lines with important fixes.
Major points
-
Pass
-
Dollar-quoting helps to simplify the syntax for string literals containing single quotes.
-
Replace
with this simpler and faster equivalent expression:
CREATE OR REPLACE FUNCTION insert_data_func()
RETURNS TRIGGER AS
$func$
BEGIN -- !
EXECUTE
$x$INSERT INTO tb_moldsummary AS t
SELECT $1.machine -- !
, $1.model
, split_part(lot, '_', 1)
, right(lot, position('_' IN lot) * -1) -- ! simpler
, COUNT(lot)
, $1.datetimestamp
FROM model$x$ || to_char(now(), 'YYYYMM') || '
WHERE lot = $1.lot
AND machine = $1.machine
GROUP BY machine, model, lot
ON CONFLICT ON CONSTRAINT tb_summary_unique
DO UPDATE
SET machine = $1.machine -- !
, totalshots = t.totalshots + 1
, datetimestamp = $1.datetimestamp'
USING NEW; -- !
RETURN NEW; -- can be RETURN NULL for AFTER trigger
END
$func$ LANGUAGE plpgsql;I marked lines with important fixes.
Major points
-
Pass
NEW to EXECUTE with the USING clause (as instructed in the answers to the question you referenced):- Insert RECORD into a table in a trigger function?
-
Dollar-quoting helps to simplify the syntax for string literals containing single quotes.
-
Replace
SUBSTRING(NEW.lot FROM (POSITION('_' in NEW.lot)+1) FOR LENGTH(NEW.lot))with this simpler and faster equivalent expression:
right(NEW.lot, position('_' IN NEW.lot) * -1)Code Snippets
CREATE OR REPLACE FUNCTION insert_data_func()
RETURNS TRIGGER AS
$func$
BEGIN -- !
EXECUTE
$x$INSERT INTO tb_moldsummary AS t
SELECT $1.machine -- !
, $1.model
, split_part(lot, '_', 1)
, right(lot, position('_' IN lot) * -1) -- ! simpler
, COUNT(lot)
, $1.datetimestamp
FROM model$x$ || to_char(now(), 'YYYYMM') || '
WHERE lot = $1.lot
AND machine = $1.machine
GROUP BY machine, model, lot
ON CONFLICT ON CONSTRAINT tb_summary_unique
DO UPDATE
SET machine = $1.machine -- !
, totalshots = t.totalshots + 1
, datetimestamp = $1.datetimestamp'
USING NEW; -- !
RETURN NEW; -- can be RETURN NULL for AFTER trigger
END
$func$ LANGUAGE plpgsql;SUBSTRING(NEW.lot FROM (POSITION('_' in NEW.lot)+1) FOR LENGTH(NEW.lot))right(NEW.lot, position('_' IN NEW.lot) * -1)Context
StackExchange Database Administrators Q#178230, answer score: 4
Revisions (0)
No revisions yet.