Update Rule on View - PostgreSQL - postgresql

My rule to update a view "fva_gpi_sbcpcba_voapcba" isn't properly updating the "product_serial_number" column in both "general_product_info" and "fva_3800" tables.
It only updates the "product_serial_number" in the first table written in the update rule and not both. That is, in this case it will update the fva_3800 table's product_serial_number, but not the general_product_info table's. If I put the UPDATE on general_product_info table first, vice versa.
Update rule: (removed overly long and unimportant code and replaced with ...)
CREATE RULE update_fva_fullview
AS ON UPDATE TO fva_gpi_sbcpcba_voapcba
DO INSTEAD
(UPDATE voa_pcba
SET (voa_pcba_serial_number, ..., ch4_voa_cqr_link)
= (NEW.voa_pcba_serial_number, ..., NEW.ch4_voa_cqr_link)
WHERE voa_pcba_serial_number = OLD.voa_pcba_serial_number;
UPDATE sbc_pcba
SET (sbc_pcba_serial_number, sbc_fw_revision, sbc_mac_address, sbc_test_result_link)
= (NEW.sbc_pcba_serial_number, NEW.sbc_fw_revision, NEW.sbc_mac_address, NEW.sbc_test_result_link)
WHERE sbc_pcba_serial_number = OLD.sbc_pcba_serial_number;
UPDATE fva_3800
SET (product_serial_number, status_pcba_serial_number, sbc_pcba_serial_number, voa_pcba_serial_number)
= (NEW.product_serial_number, NEW.status_pcba_serial_number, NEW.sbc_pcba_serial_number, NEW.voa_pcba_serial_number)
WHERE product_serial_number = OLD.product_serial_number;
UPDATE general_product_info
SET (product_serial_number, part_number, innovator_product_bom, calibration_certificate, manual,
application_sw_name, application_sw_rev, test_report_link, production_exceptions, notes)
= (NEW.product_serial_number, NEW.part_number, NEW.innovator_product_bom, NEW.calibration_certificate, NEW.manual,
NEW.application_sw_name, NEW.application_sw_rev, NEW.test_report_link, NEW.production_exceptions, NEW.notes)
WHERE product_serial_number = OLD.product_serial_number;);
View: (in full... sorry, it is long, but I'd rather not cause confusion)
CREATE OR REPLACE VIEW fva_gpi_sbcpcba_voapcba AS
SELECT product_serial_numbers.product_serial_number, general_product_info.part_number, general_product_info.innovator_product_bom,
general_product_info.calibration_certificate, general_product_info.manual, general_product_info.application_sw_name,
general_product_info.application_sw_rev, general_product_info.test_report_link, fva_3800.status_pcba_serial_number,
sbc_pcba.sbc_pcba_serial_number, sbc_pcba.sbc_fw_revision, sbc_pcba.sbc_mac_address, sbc_pcba.sbc_test_result_link,
voa_pcba.voa_pcba_serial_number, voa_pcba.voa_fw_revision, voa_pcba.voa_test_result_link, voa_pcba.ch1_tap_part_number,
voa_pcba.ch1_tap_serial_number, voa_pcba.ch1_tap_cqr_link, voa_pcba.ch1_voa_part_number, voa_pcba.ch1_voa_serial_number,
voa_pcba.ch1_voa_cqr_link, voa_pcba.ch2_tap_part_number, voa_pcba.ch2_tap_serial_number, voa_pcba.ch2_tap_cqr_link,
voa_pcba.ch2_voa_part_number, voa_pcba.ch2_voa_serial_number, voa_pcba.ch2_voa_cqr_link, voa_pcba.ch3_tap_part_number,
voa_pcba.ch3_tap_serial_number, voa_pcba.ch3_tap_cqr_link, voa_pcba.ch3_voa_part_number, voa_pcba.ch3_voa_serial_number,
voa_pcba.ch3_voa_cqr_link, voa_pcba.ch4_tap_part_number, voa_pcba.ch4_tap_serial_number, voa_pcba.ch4_tap_cqr_link,
voa_pcba.ch4_voa_part_number, voa_pcba.ch4_voa_serial_number, voa_pcba.ch4_voa_cqr_link, general_product_info.production_exceptions,
general_product_info.notes, general_product_info.gpi_id
FROM product_serial_numbers
INNER JOIN general_product_info ON product_serial_numbers.product_serial_number = general_product_info.product_serial_number
INNER JOIN fva_3800 ON product_serial_numbers.product_serial_number = fva_3800.product_serial_number
INNER JOIN sbc_pcba ON fva_3800.sbc_pcba_serial_number = sbc_pcba.sbc_pcba_serial_number
INNER JOIN voa_pcba ON fva_3800.voa_pcba_serial_number = voa_pcba.voa_pcba_serial_number;
product_serial_number is a pkey in product_serial_numbers table, and a unique fkey in general_product_info and fva_3800 tables.
edit:
fva_3800_product_serial_number_fkey FOREIGN KEY (product_serial_number) REFERENCES product_serial_numbers(product_serial_number) ON UPDATE CASCADE ON DELETE SET NULL
All of the constraints for fkeys are like this. They all have CASCADE and SET NULL.
Here is the definition of fva_3800 table:
CREATE TABLE FVA_3800 (
id serial CONSTRAINT fva_3800_id_pkey PRIMARY KEY,
product_serial_number varchar(32) UNIQUE REFERENCES Product_Serial_Numbers(product_serial_number) ON UPDATE CASCADE ON DELETE SET NULL,
status_PCBA_serial_number varchar(32),
SBC_PCBA_serial_number varchar(32) REFERENCES SBC_PCBA(SBC_PCBA_serial_number) ON UPDATE CASCADE ON DELETE SET NULL,
VOA_PCBA_serial_number varchar(32) REFERENCES VOA_PCBA(VOA_PCBA_serial_number) ON UPDATE CASCADE ON DELETE SET NULL
);

Instead of using Rules, I used Functions and Triggers.
Rules just don't work properly in this situation for UPDATEs nor DELETEs
CREATE FUNCTION update_fva_full() RETURNS TRIGGER AS $_$
BEGIN
UPDATE voa_pcba
SET (voa_pcba_serial_number, voa_fw_revision, voa_test_result_link, ch1_tap_part_number, ch1_tap_serial_number, ch1_tap_cqr_link,
ch1_voa_part_number, ch1_voa_serial_number, ch1_voa_cqr_link, ch2_tap_part_number, ch2_tap_serial_number, ch2_tap_cqr_link,
ch2_voa_part_number, ch2_voa_serial_number, ch2_voa_cqr_link, ch3_tap_part_number, ch3_tap_serial_number, ch3_tap_cqr_link,
ch3_voa_part_number, ch3_voa_serial_number, ch3_voa_cqr_link, ch4_tap_part_number, ch4_tap_serial_number, ch4_tap_cqr_link,
ch4_voa_part_number, ch4_voa_serial_number, ch4_voa_cqr_link)
= (NEW.voa_pcba_serial_number, NEW.voa_fw_revision, NEW.voa_test_result_link, NEW.ch1_tap_part_number, NEW.ch1_tap_serial_number, NEW.ch1_tap_cqr_link,
NEW.ch1_voa_part_number, NEW.ch1_voa_serial_number, NEW.ch1_voa_cqr_link, NEW.ch2_tap_part_number, NEW.ch2_tap_serial_number, NEW.ch2_tap_cqr_link,
NEW.ch2_voa_part_number, NEW.ch2_voa_serial_number, NEW.ch2_voa_cqr_link, NEW.ch3_tap_part_number, NEW.ch3_tap_serial_number, NEW.ch3_tap_cqr_link,
NEW.ch3_voa_part_number, NEW.ch3_voa_serial_number, NEW.ch3_voa_cqr_link, NEW.ch4_tap_part_number, NEW.ch4_tap_serial_number, NEW.ch4_tap_cqr_link,
NEW.ch4_voa_part_number, NEW.ch4_voa_serial_number, NEW.ch4_voa_cqr_link)
WHERE voa_pcba_serial_number = OLD.voa_pcba_serial_number;
UPDATE sbc_pcba
SET (sbc_pcba_serial_number, sbc_fw_revision, sbc_mac_address, sbc_test_result_link)
= (NEW.sbc_pcba_serial_number, NEW.sbc_fw_revision, NEW.sbc_mac_address, NEW.sbc_test_result_link)
WHERE sbc_pcba_serial_number = OLD.sbc_pcba_serial_number;
UPDATE fva_3800
SET (product_serial_number, status_pcba_serial_number, sbc_pcba_serial_number, voa_pcba_serial_number)
= (NEW.product_serial_number, NEW.status_pcba_serial_number, NEW.sbc_pcba_serial_number, NEW.voa_pcba_serial_number)
WHERE product_serial_number = OLD.product_serial_number;
UPDATE general_product_info
SET (product_serial_number, part_number, innovator_product_bom, calibration_certificate, manual,
application_sw_name, application_sw_rev, test_report_link, production_exceptions, notes)
= (NEW.product_serial_number, NEW.part_number, NEW.innovator_product_bom, NEW.calibration_certificate, NEW.manual,
NEW.application_sw_name, NEW.application_sw_rev, NEW.test_report_link, NEW.production_exceptions, NEW.notes)
WHERE product_serial_number = OLD.product_serial_number;
RETURN OLD;
END $_$ LANGUAGE 'plpgsql';
CREATE TRIGGER update_from_fva_view
INSTEAD OF UPDATE ON fva_gpi_sbcpcba_voapcba
FOR EACH ROW EXECUTE PROCEDURE update_fva_full();

Related

COALESCE failing following CTE Deletion. (PostgreSQL)

PostgreSQL 11.1 PgAdmin 4.1
This works some of the time:
BEGIN;
SET CONSTRAINTS ALL DEFERRED;
WITH _in(trx, lastname, firstname, birthdate, old_disp, old_medname, old_sig, old_form, new_disp, new_medname, new_sig, new_form, new_refills) AS (
VALUES ('2001-06-07 00:00:00'::timestamp,
UPPER(TRIM('JONES')), UPPER(TRIM('TOM')), '1952-12-30'::date,
64::integer,
LOWER(TRIM('adipex 37.5mg tab')), LOWER(TRIM('one tab po qd')), LOWER(TRIM('tab')),
63::integer,
LOWER(TRIM('adipex 37.5mg tab')), LOWER(TRIM('one tab po qd')), LOWER(TRIM('tab')),
33::integer
)
),
_s AS ( -- RESOLVE ALL SURROGATE KEYS.
SELECT n.*, d1.recid as old_medication_recid, d2.recid as new_medication_recid, pt.recid as patient_recid
FROM _in n
JOIN medications d1 ON (n.old_medname, n.old_sig, n.old_form) = (d1.medname, d1.sig, d1.form)
JOIN medications d2 ON (n.new_medname, n.new_sig, n.new_form) = (d2.medname, d2.sig, d2.form)
JOIN patients pt ON (pt.lastname, pt.firstname, pt.birthdate) = (n.lastname, n.firstname, n.birthdate)
),
_t AS ( -- REMOVE CONFLICTING RECORD, IF ANY.
DELETE FROM rx r
USING _s n
WHERE (r.trx::date, r.disp, r.patient_recid, r.medication_recid)=(n.trx::date, n.new_disp, n.patient_recid, n.new_medication_recid)
RETURNING r.*
),
_u AS( -- GET NEW SURROGATE KEY.
SELECT COALESCE(_t.recid, r.recid) as target_recid, r.recid as old_recid
FROM _s n
JOIN rx r ON (r.trx, r.disp, r.patient_recid, r.medication_recid) = (n.trx, n.old_disp, n.patient_recid, n.old_medication_recid)
LEFT JOIN _t ON (_t.trx::date, _t.disp, _t.patient_recid, _t.medication_recid) = (n.trx::date, n.new_disp, n.patient_recid, n.new_medication_recid)
)
UPDATE rx r -- UPDATE ORIGINAL RECORD WITH NEW VALUES.
SET disp = n.new_disp, medication_recid = n.new_medication_recid, refills = n.new_refills, recid = _u.target_recid
FROM _s n, _u
WHERE r.recid = _u.old_recid
RETURNING r.*;
COMMIT;
Where table rx is defined as:
CREATE TABLE phoenix.rx
(
recid integer NOT NULL DEFAULT nextval('rx_recid_seq'::regclass),
trx timestamp without time zone NOT NULL,
disp integer NOT NULL,
refills integer,
tprinted timestamp without time zone,
tstop timestamp without time zone,
modified timestamp without time zone DEFAULT now(),
patient_recid integer NOT NULL,
medication_recid integer NOT NULL,
dposted date NOT NULL,
CONSTRAINT pk_rx_recid PRIMARY KEY (recid),
CONSTRAINT rx_unique UNIQUE (dposted, disp, patient_recid, medication_recid)
DEFERRABLE,
CONSTRAINT rx_medication_fk FOREIGN KEY (medication_recid)
REFERENCES phoenix.medications (recid) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE RESTRICT
DEFERRABLE,
CONSTRAINT rx_patients FOREIGN KEY (patient_recid)
REFERENCES phoenix.patients (recid) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE RESTRICT
)
After many hours, it is found that the "Delete.." of a conflicting record works as expected, but the "COALESCE" STATEMENT seems to fail when deciding on the new surrogate key (primary key) of rx.recid -- it does not seem to receive the result of the delete. (Or maybe the timing is wrong???)
Any help would be most appreciated.
TIA
This is documented:
The sub-statements in WITH are executed concurrently with each other and with the main query. Therefore, when using data-modifying statements in WITH, the order in which the specified updates actually happen is unpredictable. All the statements are executed with the same snapshot (see Chapter 13, so they cannot “see” one another's effects on the target tables.
Don't use the same table twice in a statement with a CTE if it occurs in a DML statement. Rather, use DELETE ... RETURNING and use the returned values in the other parts of the statement.
If you cannot rewrite the statement like that, use more than one statement instead of putting everything into a single CTE.
#LaurenzAlbe is totally correct in his answer. Below is a working solution to my problem. There are a few things to note:
The unique constraint is formed on a column in rx defined as a date and created by a trigger on update/insert that casts the timestamp of trx to a date: as in trx::date. For reasons I am not clear on, using r.trx::date in place of r.dposted leads to many records being identified and not the one record I want. Not sure why???. So the first fix was to use r.dposted, not r.trx::date.
Although the cte's are designed to be independent of each other, by using "RETURNING..." and incorporating the cte's in a step-wise fashion, one can be built upon another to obtain a final result set.
The working code is:
WITH _in(trx, lastname, firstname, birthdate, old_disp, old_medname, old_sig, old_form, new_disp, new_medname, new_sig, new_form, new_refills) AS (
VALUES ('2001-06-07 00:00:00'::timestamp,
UPPER(TRIM('smith')), UPPER(TRIM('john')), '1957-12-30'::date,
28::integer,
LOWER(TRIM('test')), LOWER(TRIM('i am sig')), LOWER(TRIM('tab')),
28::integer,
LOWER(TRIM('test 1')), LOWER(TRIM('i am sig')), LOWER(TRIM('tab')),
8::integer
)
),
_m AS (
SELECT n.*, d1.recid as old_medication_recid, d2.recid as new_medication_recid, pt.recid as patient_recid
FROM _in n
JOIN patients pt ON (pt.lastname, pt.firstname, pt.birthdate) = (n.lastname, n.firstname, n.birthdate)
JOIN medications d1 ON (n.old_medname, n.old_sig, n.old_form) = (d1.medname, d1.sig, d1.form)
LEFT JOIN medications d2 ON (n.new_medname, n.new_sig, n.new_form) = (d2.medname, d2.sig, d2.form)
),
_t AS ( -- REMOVE CONFLICTING RECORD, IF ANY.
DELETE FROM rx r
USING _m
WHERE (r.dposted, r.disp, r.patient_recid, r.medication_recid) = (_m.trx::date,_m.new_disp, _m.patient_recid, _m.new_medication_recid)
RETURNING r.*
),
_s AS ( -- GET NEW SURROGATE KEY
SELECT _m.*, r1.recid as old_recid, r2.recid as new_recid, COALESCE(r2.recid, r1.recid) as target_recid
FROM _m
JOIN rx r1 ON (r1.dposted, r1.disp, r1.patient_recid, r1.medication_recid) = (_m.trx::date,_m.old_disp, _m.patient_recid, _m.old_medication_recid)
LEFT JOIN rx r2 ON (r2.dposted, r2.disp, r2.patient_recid, r2.medication_recid) = (_m.trx::date,_m.new_disp, _m.patient_recid, _m.new_medication_recid)
LEFT JOIN _t ON (_t.recid = r2.recid)
)
UPDATE rx -- UPDATE ORIGINAL RECORD WITH NEW VALUES.
SET disp = _s.new_disp, medication_recid = _s.new_medication_recid, refills = _s.new_refills, recid = _s.target_recid
FROM _s
WHERE rx.recid = _s.old_recid
RETURNING rx.*;
COMMIT;
Hope this helps somebody.

What is the correct syntax to write "merge statement" in PostgreSQL 9.6.2

code :It is merge query which is running on Postgres 9.6.2 and giving syntax error.
<<!--It is giving syntax error--->
MERGE INTO timesheets.timesheet_report AS tgt USING timesheets.tmp_timesheet_report AS src ON src.FMNo = tgt.FMNo
AND src.ts_start_dt = tgt.ts_start_dt
AND src.charge_code = tgt.charge_code WHEN NOT MATCHED
INSERT (tgt.FIRST_NAME,
tgt.LAST_NAME)
VALUES(src.FIRST_NAME,
src.LAST_NAME) WHEN MATCHED
UPDATE
SET tgt.FIRST_NAME = src.FIRST_NAME,
tgt.LAST_NAME = src.LAST_NAME;
It is ON CONFLICT
INSERT INTO table_name [your usual insert syntax here]
ON CONFLICT [some conflict definition]
DO UPDATE SET column1 = EXCLUDED.value1
So I guess your query would look like this:
INSERT INTO timesheets.timesheet_report (FMNo, ts_start_dt, charge_code, FIRST_NAME, LAST_NAME)
SELECT src.FMNo, src.ts_start_dt, src.charge_code, src.FIRST_NAME, src.LAST_NAME FROM timesheets.tmp_timesheet_report AS src
ON CONFLICT (FMNo, ts_start_dt, charge_code)
DO UPDATE
SET FIRST_NAME = EXCLUDED.FIRST_NAME,
LAST_NAME = EXCLUDED.LAST_NAME;
If you don't have primary key or unique index, then you need to create unique index on timesheets.timesheet_report using btree (FMNo, ts_start_dt, charge_code);

How do I add a trigger to populate ModifiedDate

is it possible to have the ModifiedDate automatically update with the CurrentDateTime when the row has been updated/changed?
SELECT [ID]
,[PK]
,[Iteration]
,[Exam01]
,[Exam02]
,[Exam03]
,[Exam04]
,[Exam05]
,[Exam06]
,[Exam07]
,[Exam08]
,[Exam09]
,[Exam10]
,[Exam11]
,[Exam12]
,[Exam13]
,[Exam14]
,[HRGCode]
,[AgeStart]
,[AgeEnd]
,[CreateDate]
,[ModifiedDate] --- Need this to update when the row is Modified!
,[ModifiedBy]
,[Comments]
FROM [CommDB].[dbo].[tblRadiologyNucMedLookup] c
order by ID
Do you really need a trigger for this? You can have that column in the same UPDATE statement like
update tblRadiologyNucMedLookup
set Iteration = 10,
[ModifiedDate] = getdate()
where ID = 101;

How to refer OLD.X at ON CONFLICT DO UPDATE SET X = OLD.X + EXCLUDED.X

There are an alias for "old value" in the ON CONFLICT DO UPDATE?
My real life problem is
INSERT INTO art.validterm (namespace,term,X,info)
SELECT namespace,term,array_agg(Xi), 'etc'
FROM term_raw_Xs
GROUP BY namespace,term
ON CONFLICT (term) DO
UPDATE SET aliases=OLD.X||EXCLUDED.X
WHERE term=EXCLUDED.term
PS: no "OLD" exists, is the question. The parser say that only X is ambigous.
Simply replacing OLD with the name of the table, in your case: validterm, worked for me.
My test:
DROP TABLE IF EXISTS work.term_raw;
CREATE TABLE work.term_raw
(
unique_field INT UNIQUE,
x_field TEXT
);
INSERT INTO work.term_raw VALUES (1, 'A');
INSERT INTO work.term_raw VALUES (1, 'B')
ON CONFLICT (unique_field) DO UPDATE SET x_field = term_raw.x_field || EXCLUDED.x_field;
SELECT * FROM work.term_raw;
My result:

Cannot insert NULL value into column error

I have a issue where I want to update a column in a table and with a trigger to update same column but in another table. It says I cannot insert NULL but I can't seem to understand from where it gets that NULL value. This is the trigger:
CREATE TRIGGER Custom_WF_Update_WF_DefinitionSteps_DefinitionId ON WF.Definition
AFTER UPDATE AS BEGIN
IF UPDATE(DefinitionId)
IF TRIGGER_NESTLEVEL() < 2
BEGIN
ALTER TABLE WF.DefinitionSteps NOCHECK CONSTRAINT ALL
UPDATE WF.DefinitionSteps
SET DefinitionId =
(SELECT i.DefinitionId
FROM inserted i,
deleted d
WHERE WF.DefinitionSteps.DefinitionId = d.DefinitionId
AND i.oldPkCol = d.DefinitionId)
WHERE WF.DefinitionSteps.DefinitionId IN
(SELECT DefinitionId FROM deleted)
ALTER TABLE WF.DefinitionSteps CHECK CONSTRAINT ALL
END
END
This update statement works just fine:
UPDATE [CCHMergeIntermediate].[WF].[Definition]
SET DefinitionId = source.DefinitionId + 445
FROM [CCHMergeIntermediate].[WF].[Definition] source
But this one fails:
UPDATE [CCHMergeIntermediate].[WF].[Definition]
SET DefinitionId = target.DefinitionId
FROM [CCHMergeIntermediate].[WF].[Definition] source
INNER JOIN [centralq3].[WF].[Definition] target
ON (((source.Name = target.Name) OR (source.Name IS NULL AND target.Name IS NULL)))
I get the following error:
Msg 515, Level 16, State 2, Procedure Custom_WF_Update_WF_DefinitionSteps_DefinitionId, Line 7
Cannot insert the value NULL into column 'DefinitionId', table 'CCHMergeIntermediate.WF.DefinitionSteps'; column does not allow nulls. UPDATE fails.
If I do a select instead of the update statement, like this:
SELECT source.DefinitionId, target.DefinitionId
FROM [CCHMergeIntermediate].[WF].[Definition] source
INNER JOIN [centralq3].[WF].[Definition] target
ON (((source.Name = target.Name) OR (source.Name IS NULL AND target.Name IS NULL)))
I get this result:
http://i.stack.imgur.com/3cZsM.png (sorry for external link, I don't have enaugh reputation to post image here )
What am I doing wrong? What I don't see? What am I missing..?
The problem was in the trigger at the condition. I modified the second where from i.oldPkCol = d.DefinitionId to i.oldPkCol = **d.oldPkCol** and it worked.
UPDATE WF.DefinitionSteps
SET DefinitionId =
(SELECT i.DefinitionId
FROM inserted i,
deleted d
WHERE WF.DefinitionSteps.DefinitionId = d.DefinitionId
AND i.oldPkCol = **d.oldPkCol**)
WHERE WF.DefinitionSteps.DefinitionId IN
(SELECT DefinitionId FROM deleted)