lundi 17 septembre 2018

PostgreSQL UPDATE trigger using IF/ELSE statements and SELECT subquery

I have a central table in a library-like database called deployments_retrievals. This table stores all 'deployment' (e.g., checkouts) and 'retrieval' (e.g., returns) events. I next have a table of metadata_<gear>, containing metadata such as gear serial number. Finally, I have a third table containing deployment information for each specific gear type, deployment_<gear>, which stores information about each gear item before and after each deployment event. This third table is used to link each gear serial number to a deployment event id deployments_retrievals.deploy_id.

What I'd like to do is update metadata_<gear>.current_location whenever deployments_retrievals is updated. I can successfully update the gear table when one condition is met; however, I'm having trouble understanding the syntax for updating when multiple conditions are involved - essentially I'm having trouble combining postgresql UPDATE, IF/ELSEIF/ELSE, and a subquery. I'm looking for something along these lines:

create or replace function trigger_update_gearlocation()
returns trigger as 
$body$
begin

-- If deployments_retrievals status is deployed (0), retrieved (1), or lost (2), then updated the <gear>_locations table with appropriate current_location. 

update metadata_receivers
    if DEPLOY_STATUS.retrieval_status = 0 then
        set metadata_receivers.current_location = 'Deployed'

    elseif DEPLOY_STATUS.retrieval_status = 1 then
        set metadata_receivers.current_location = 'Retrieved'

    elseif DEPLOY_STATUS.retrieval_status = 2 then
        set metadata_receivers.current_location = 'Lost'

-- This selects all receiver serial numbers that are DEPLOYED, RETRIEVED, or LOST, + their last deploy time as "DEPLOY_STATUS".
    from (
        select deployment_receiver.receiver_sn as serial_number, deployments_retrievals.retrieval_status, deployments_retrievals.deploy_datetime
        from deployment_receiver inner join deployments_retrievals 
            on deployment_receiver.deploy_id = deployments_retrievals.deploy_id
        --where retrieval_status <> 1
        ) as DEPLOY_STATUS

    where DEPLOY_STATUS.serial_number = metadata_receivers.receiver_sn;

end;
$body$
language plpgsql;

CREATE TRIGGER "deployLocations" AFTER update ON deployments_retrievals FOR EACH ROW EXECUTE 
PROCEDURE trigger_update_gearlocation();

My three tables, for reproducibility:

-- 1. The table to be updated. Contains metadata on the gear, including current_location.

CREATE TABLE metadata_receivers
(
receiver_sn         integer PRIMARY KEY, 
current_location    varchar (255), -- Trigger "trigger_update_gearlocation()" updates column if deployment status is 0, receiver status is "deployed"; 1 - "retrieved"; 2 - "lost"
date_retired        date,
date_updated        timestamp DEFAULT current_timestamp
);

-- 2. Deployments & retrievals table - stores all 'checkout' and 'return' events for all types of gear. 

CREATE TABLE deployments_retrievals
(
deploy_id           bigserial primary key,
deploy_datetime     timestamptz NOT NULL, 
retrieval_status    integer check (retrieval_status in ('0', '1', '2')) NOT NULL DEFAULT '0',
retrieval_datetime  timestamptz check (retrieval_datetime > deploy_datetime), 
date_updated        timestamp DEFAULT current_timestamp
);

-- 3. Deployment information specifically related to receivers only. Used to link the receiver metadata to individual deployment events. 

CREATE TABLE deployment_receiver
(
receiverdeploy_id   bigserial primary key,
receiver_sn         integer REFERENCES metadata_receivers(receiver_sn) on update cascade on delete cascade not null,
deploy_id           integer REFERENCES deployments_retrievals(deploy_id) on update cascade on delete cascade not null,
-- ... various misc fields
date_updated        timestamp DEFAULT current_timestamp,
unique(receiver_sn, deploy_id)
);

Any notes on whether or not this is best practice or how to streamline would be appreciated.

Aucun commentaire:

Enregistrer un commentaire