Reputation: 59
I have 100s of tables having the same schema and I have a trigger function to UPDATE some column whenever data is INSERTed into that table.
Table Schema:
CREATE TABLE symbol_daily_ohlc (
cdate date,
open numeric(8,2),
high numeric(8,2),
low numeric(8,2),
close numeric(8,2),
sma8 numeric(8,2)
);
Trigger function:
create or replace function update_sma8() RETURNS TRIGGER AS
$$
BEGIN
UPDATE symbol_daily_ohlc d SET sma8 = s.simple_mov_avg
FROM
(
SELECT sec.cdate,AVG(sec.close)
OVER(ORDER BY sec.cdate ROWS BETWEEN 7 PRECEDING AND CURRENT ROW) AS
simple_mov_avg FROM symbol_daily_ohlc sec
)s where s.cdate = NEW.cdate --The newly inserted cdate
AND d.cdate = s.cdate;
RETURN NULL;
END $$ language plpgsql;
Trigger setup on table:
CREATE TRIGGER trig_update_sma
AFTER INSERT ON symbol_daily_ohlc
FOR EACH ROW
EXECUTE PROCEDURE update_sma8();
This is working well for the given table i.e symbol_daily_ohlc. I would like to use the same trigger function i.e update_sma8() to be used with any table having the same schema (I don't want to rewrite the same function for different tables).
I tried replacing the table name (i.e symbol_daily_ohlc) with TG_TABLE_NAME, but that didn't work - thrown errors. So how to do that?
Reference: SQL trigger function to UPDATE daily moving average upon INSERT
Upvotes: 1
Views: 3099
Reputation: 31648
You can have a same procedure that executes and returns a Trigger for all the tables, but you can't have a same Trigger for all your tables.
Here's a block that dynamically creates a Trigger with table name suffix ( using EXECUTE format
)
DO $$
declare
tabs RECORD;
BEGIN
for tabs IN
(select table_name,table_schema
from information_schema.tables where table_name
like 'symbol_daily_ohlc%'
-- and table_schema like '%'
) LOOP
EXECUTE format('CREATE TRIGGER check_update_%I
AFTER INSERT ON %I.%I
FOR EACH ROW
EXECUTE PROCEDURE update_sma8()',tabs.table_name,
tabs.table_schema
,tabs.table_name);
END LOOP;
END $$;
And here's your Trigger, which takes the table name dynamically from TG_TABLE_NAME
create or replace function update_sma8() RETURNS TRIGGER AS
$$
BEGIN
EXECUTE format ('UPDATE %I d SET sma8 = s.simple_mov_avg
FROM
(
SELECT sec.cdate,AVG(sec.close)
OVER(ORDER BY sec.cdate ROWS BETWEEN 7 PRECEDING AND CURRENT ROW) AS
simple_mov_avg FROM %I sec
)s where s.cdate = %L --The newly inserted cdate
AND d.cdate = s.cdate',TG_TABLE_NAME,TG_TABLE_NAME,NEW.cdate);
RETURN NULL;
END $$ language plpgsql;
As others have suggested, it's not a good idea to have multiple tables with the same structure. You should consider combining them into one table.
Upvotes: 1
Reputation: 74605
I think you'll probably have to use SQL to generate SQL, then run the generated SQL. Something like:
select CONCAT('create or replace function update_sma8() RETURNS TRIGGER AS
$$
BEGIN
UPDATE ', table_name, ' d SET sma8 = s.simple_mov_avg
FROM
(
SELECT sec.cdate,AVG(sec.close)
OVER(ORDER BY sec.cdate ROWS BETWEEN 7 PRECEDING AND CURRENT ROW) AS
simple_mov_avg FROM ', table_name, '
)s where s.cdate = NEW.cdate --The newly inserted cdate
AND d.cdate = s.cdate;
RETURN NULL;
END $$ language plpgsql;
') from information_schema.tables
But really you should consider Gordon's advice (and my followup) and put all your data back in one table:
SELECT CONCAT('INSERT INTO all_hist SELECT ''', table_name, ''', t.* FROM ', table_name) FROM information_schema.tables
This will generate a bunch of sqls that put all the table data into all_hist, a table that should have an identical schema to the other thousands, except for an additional column "symbol" or whatever..
You can use a similar trick to create yourself a bunch of views to recreate the thousands-table approach if you really want..
Upvotes: 0