0

I've been looking for a solution to this on other threads however I'm getting just bits and pieces of what I need from other topics which I can't tie together to work correctly.

The background of the problem is this: Each night a transaction log is written to a newly created table in the database, then the primary log is cleared. The name of the table created isn't related to the date (making searching for historical logs difficult - so the table name for a given date can't be guessed or hard coded).

However once the transactions are moved into the new table, a logging table has a new record added to it which stores the dates and other information about the archived records (including the name of the table).

I'm trying to write a function which allows me to pass a date range (start date and end date) into it, then return the name of all tables which have information within the given date range - storing the names in an array. Once the array is populated a SQL query is run against each table in the array to write all of this to a single temporary table, allowing it to be queried.

Some pseudo code would be:

CREATE OR REPLACE FUNCTION transactions(begin_date varchar(10), end_date varchar(10)) RETURN void as $$
Begin
SELECT INTO table_names 'name' from 'log_table' where 'log_date' between begin_date and end_date;
FOREACH table_names LOOP
SQL_Query = "INSERT INTO temporary_table SELECT * from " + table_names;
EXECUTE SQL_Query;
END LOOP;

Any help is appreciated - my logic could be entirely broken on this and I'll need to change my methodology.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Bizmark
  • 131
  • 4
  • Why are you wanting to make use of an array? – Kuberchaun Jul 21 '14 at 15:50
  • The number of 'table names' being returned is not linear, given that the function is passing through different date ranges every time - so the control structure has to allow for any number of returned column values. As I said, I may be going about this the wrong way altogether. – Bizmark Jul 22 '14 at 10:41
  • I'm not completely following your description above, but can't you, if needed dump your temporary data set directly into a temp table(http://www.postgresql.org/docs/9.3/static/sql-createtable.html)? Temp tables will be visible within the life of your session. I can't see where the array is the most straight forward approach assuming you need to temporarily store your data for querying. – Kuberchaun Jul 22 '14 at 13:09
  • Appreciate the time you're taking to look at this - if you could provide an example of how using table would achieve what i'm aiming for I can try that. As it stands, i don't mind using a temp table - whatever works! – Bizmark Jul 22 '14 at 16:37

1 Answers1

0
CREATE OR REPLACE FUNCTION f_create_temp_trans(_begin date
                                              ,_end   date, OUT _rows int) AS
$func$
BEGIN

DROP TABLE IF EXISTS temp_trans;
CREATE TEMP TABLE IF NOT EXISTS temp_trans (LIKE existing_tbl);

EXECUTE (
   SELECT E'INSERT INTO temp_trans\NSELECT * FROM '
       || string_agg(quote_ident(tbl_name)
                   , E'\NUNION ALL SELECT * FROM '
                     ORDER  BY log_date)
   FROM   log_table
   WHERE  log_date BETWEEN _begin AND _end
   );

GET DIAGNOSTICS _rows = ROW_COUNT;

END
$func$ LANGUAGE plpgsql;

Call:

SELECT * FROM f_create_temp_trans('2014-06-01', '2014-06-11');

Returns the number of rows inserted into the temp table.

Major points

  • Creates a temp table (pre-emptive) named temp_trans (lives for the duration of the session). Structure is copied form existing_tbl - replace with an actual table name.

  • Using the aggregate function string_agg() to compose a single INSERT with UNION ALL, which is more efficient than running many INSERT statements.

  • Produces and executes a statement of the form:

    INSERT INTO temp_trans
    SELECT * FROM tbl1
    UNION ALL SELECT * FROM "trickY name tbl2"
    UNION ALL SELECT * FROM tbl3
    ...
    
  • Single quotes ('val') are for values, not for identifiers, where you use double-quoted if necessary.

  • Why are your dates varchar(10)? Using date in my code like it should be.

  • About GET DIAGNOSTICS.

  • E'\N' is a line break. Just to prettify the generated SQL code for debugging. Not needed. You can replace with blanks:

    ...
    SELECT 'INSERT INTO temp_trans SELECT * FROM '
        || string_agg(quote_ident(tbl_name), ' UNION ALL SELECT * FROM ' ...
    

    About quoting and escape strings:

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • Extremely well thought out answer - I'm working through it just now. To answer your question, the dates are stored as varchars but a third-party piece of software. This was not my choice. – Bizmark Jul 23 '14 at 11:11
  • Worked perfectly - I had to replace the '/N' with '/ ' - this is probably a quirk of the version I'm using. – Bizmark Jul 23 '14 at 11:49