Like detailed in my first question I have a Postgres 9.1 database with multiple tables that have the exact same column names, they only vary in their column values:
tbl_log_a
tbl_log_b
tbl_log_c
...
26 tables (from a to z). Each table has a trigger that calls a trigger function named trfn_tbl_log_%letter% (from a to z) which does the exact same thing:
CREATE OR REPLACE FUNCTION trfn_tbl_log_a_timetypespan()
RETURNS trigger AS
$BODY$
DECLARE
v_timetype character varying;
v_timestmp_timetype timestamp without time zone;
v_timetypespan_resume interval;
v_stmtserial real;
v_sumtimetypespan_fnname interval;
BEGIN
IF NEW.timetype = 'lap' THEN
SELECT timetype, timestmp, timetypespan FROM tbl_log_a WHERE fnname = NEW.fnname AND (timetype = 'start' OR timetype = 'resume') ORDER BY stmtserial DESC LIMIT 1 INTO v_timetype, v_timestmp_timetype, v_timetypespan_resume;
IF v_timetype = 'start' THEN
NEW.timetypespan := age(NEW.timestmp, v_timestmp_timetype);
ELSIF v_timetype = 'resume' THEN
SELECT timestmp FROM tbl_log_a WHERE fnname = NEW.fnname AND timetype = 'start' ORDER BY stmtserial DESC LIMIT 1 INTO v_timestmp_timetype;
NEW.timetypespan := age(NEW.timestmp, v_timestmp_timetype) - v_timetypespan_resume;
ELSE
RAISE EXCEPTION USING MESSAGE = 'There is not any previous row...';
END IF;
ELSIF NEW.timetype = 'resume' THEN
SELECT timestmp FROM tbl_log_a WHERE fnname = NEW.fnname AND (timetype = 'start' OR timetype = 'lap') ORDER BY stmtserial DESC LIMIT 1 INTO v_timestmp_timetype;
IF FOUND THEN
NEW.timetypespan := age(NEW.timestmp, v_timestmp_timetype);
ELSE
RAISE EXCEPTION USING MESSAGE = 'There is not any previous row...';
END IF;
ELSIF NEW.timetype = 'total' THEN
SELECT stmtserial FROM tbl_log_a WHERE fnname = NEW.fnname AND timetype = 'total' ORDER BY stmtserial DESC LIMIT 1 INTO v_stmtserial;
SELECT SUM(timetypespan) FROM (SELECT DISTINCT ON (floor(timeidx)::int) floor(timeidx)::int timeidx, timetypespan
FROM tbl_log_a WHERE fnname = NEW.fnname AND timetype = 'lap' AND stmtserial > coalesce(v_stmtserial, 0) ORDER BY 1, 2 DESC) a INTO v_sumtimetypespan_fnname;
IF v_sumtimetypespan_fnname NOTNULL THEN
NEW.timetypespan := v_sumtimetypespan_fnname;
ELSE
RAISE EXCEPTION USING MESSAGE = 'There is not any previous row...';
END IF;
END IF;
return NEW;
END
$BODY$
LANGUAGE plpgsql VOLATILE;
Trigger definition:
CREATE TRIGGER trfn_tbl_log_a_timetypespan
BEFORE INSERT ON tbl_log_a
FOR EACH ROW EXECUTE PROCEDURE trfn_tbl_log_a_timetypespan();
So I have to create 26 trigger functions, one for each tbl_log_%letter%. I am trying to replace all of them with a single, generic trigger function.
We worked out a solution with dynamic SQL under my previous question with a simplified function. Can the same technique be extended to this more complex scenario?
EXECUTE format($$...
floor()withtrunc(). What's the data type oftimeidx? – Erwin Brandstetter Aug 31 '15 at 00:38timeidxisreal. Also I have tried to see why the secondSELECTin my firstELSIFbranch was a repetition of theSELECTone level above, you mean this right?SELECT timetypespan FROM tbl_log_a WHERE fnname = NEW.fnname AND timetype = 'resume' ORDER BY stmtserial DESC LIMIT 1 INTO v_timetypespan_resume;is selectingtimetypespan, the one level above line is selectingtimetypeboth are different columns, do you meaned that?. Thanks Advanced. – Natysiu16 Aug 31 '15 at 00:58trunc()should just work and is a bit faster. It's really not important, it was just an aside. See fiddle: http://sqlfiddle.com/#!15/9eecb7db59d16c80417c72d1e1f4fbf1/2893. 2. Yes, different columns, but same row. I select both columns at once which costs almost nothing, while executing another query is expensive. – Erwin Brandstetter Aug 31 '15 at 01:17SELECTit selectsWHERE (timetype = 'start' OR timetype = 'resume')and in the secondSELECT(the first ELSIF statement) it selectsWHERE timetype = resumeare they the same row? do you mean those two, right? You mean I could addtimetypespanto the firstSELECTto replace the secondSELECTam I right? Could you please very briefly tell me how would you shrink that part of my script without using the "dynamically determined table name" just using the proper table names Thanks Advanced. – Natysiu16 Aug 31 '15 at 05:59CASE _timetype ... WHEN 'resume' THENmeans that, in this case, the first row according to your sort actually hastimetype = 'resume', you don't have to run the query another time. Test it, it should do the same, just faster. You have to test it anyway, you can't just implement a solution from a friendly stranger on the internet. I added yet more explanation. – Erwin Brandstetter Aug 31 '15 at 21:56SELECT. I replaced it in the firstSELECT, tested and it worked very well, though I still had to create a variable to store thetimetypespan. (shown in the question). I saw that there are some pros and cons regarded to performance when using this dynamically set tablename method. Given the current escenario which one would you suggest? The dynamically set tablename method or having one Trigger Function for each different table name? (I have joined all my 2 Trigger Functions in only one now) Thanks Advanced. – Natysiu16 Aug 31 '15 at 22:46INSERTmany rows in the same session, static SQL may prove a bit faster, because query plans are reused like with prepared statements. The dynamic variant is more complex, you need someone to understand plpgsql properly. If that is no problem andINSERTperformance is either not critical or you do not insert many rows in the same session, the dynamic variant is more elegant and may even be faster. One function instead of 26 has a couple of general benefits. – Erwin Brandstetter Aug 31 '15 at 23:00