1

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

9
  • 1
    You shouldn't have tables with the same schema. If you are thinking of such a thing, you probably have an issue with your data model. All that data should go into a single table with a column that defines whatever other attribute you want the data to have. Commented Jan 12, 2019 at 12:57
  • The database is to store historical data of stock market i.e of different symbols (stocks). I think this is better approach to study the particular stock. Commented Jan 12, 2019 at 13:01
  • And yet, having a column called "stock_symbol" would have meant this issue wouldn't have been. You can still have a million "tables" if you want.. CREATE VIEW msft_history AS SELECT * FROM all_stock_history WHERE stock_symbol = 'MSFT';CREATE VIEW goog_history AS SELECT * FROM all_stock_history WHERE stock_symbol = 'GOOG' Commented Jan 12, 2019 at 13:56
  • Instead of an UPDATE assign the values you want to the respective columns of the new pseudo record. Then you won't have a table name in the trigger body and can use it for every table that has the columns you address (with compatible types of course). Commented Jan 12, 2019 at 14:07
  • 1
    @GordonLinoff I accepted your suggestion and I have changed my database accordingly. Thanks to you and Caius Jard. Commented Jan 13, 2019 at 9:24

2 Answers 2

1

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;

Demo

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.

Sign up to request clarification or add additional context in comments.

1 Comment

That's so nice of you to answer. Thank you. I would evaluate others suggestion too.
0

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..

1 Comment

Thank you for answering. I would evaluate your suggestion.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.