Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.

Re: DBI - Table names & placeholders

by ikegami (Patriarch)
on Apr 24, 2010 at 05:09 UTC ( [id://836642]=note: print w/replies, xml ) Need Help??

in reply to DBI - Table names & placeholders

If you're worried about unusual table names, you need $dbh->quote_identifier.

for my $table (qw( Widgets201003 Widgets201004 Widgets201005 )) { my $q_table = $dbh->quote_identifier($table); my $sth = $dbh->prepare(" INSERT INTO $q_table ( moo, baa, cluck ) VALUES ( ?, ?, ? ) "); ... }

Replies are listed 'Best First'.
Re^2: DBI - Table names & placeholders
by McDarren (Abbot) on Apr 24, 2010 at 06:17 UTC
    If you're worried about unusual table names
    Well, no - that's not my problem.
    The problem is that I will not know the names of the tables until run-time.
    Let me give another (once again, contrived) example, that might explain it a bit better.

    Imagine that I have two sets of tables, one a set of transactional records (eg, sales records), and the other a set of consolidated tables for reporting. So I have a Sales table with attributes for Date/Time, ItemID & Price.

    Then I have a set of tables that look like Sales201003, Sales201004, Sales201005, etc - a separate table for each year/month combination. (This is not the way I would design it - but this is how it is and so I need to deal with it).

    The SalesYYYYMM tables have one row per day, per item, along with attributes for the total number sold, and total revenue.

    So I have code that selects records from the Sales table, and then inserts summarised data into the appropriate SalesYYYYMM tables. The code I would _like_ to be able to write would look something like this:

    # Some code here which selects data from Sales table # ... # ... # then... my @sales_inserts; my $insert_sql = 'INSERT INTO ? (ItemID, Day, Revenue, Count) VALUES ( +?, ?, ?, ?); my $sales_insert_dbq = $sales_dbh->prepare($insert_sql); while (my @data = $dbq->fetchrow_array) { my ($year, $month, $day, $itemid, $revenue, $count) = @data; my $sales_table = "Sales$year$month"; my @insert_values = ($sales_table, $day, $itemid, $revenue, $count +); push @sales_inserts, [@insert_values]; } eval { for my $i (0 .. $#sales_inserts) { $sales_insert_dbq->execute(@{$sales_inserts[$i]}); } $sales_dbh->commit; }; if ($@) { # Error handling, rollback, etc }
    But the above won't work because I can't use a placeholder for a table name.
    I can work around it easily enough by creating a separate statement handle for each YYYYMM combination.
    I'm just not sure what the best approach to that would be.

    Oh, and please ignore the fact that it's horrible data base design, and that calculated data is being written back into the data base. As I said, it's a contrived example that helps to describe my problem.

      The bottom loop should be

      for (@$sales_inserts) { my $table = shift(@$_); my $sth = $dbh->prepare(' INSERT INTO '.$dbh->quote_identifier($table).' ( ItemID, Day, Revenue, Count ) VALUES ( ?, ?, ?, ? ) '); $sth->execute(@$_); }
      my %sth_cache; for (@$sales_inserts) { my $table = shift(@$_); my $sth = $sth_cache{$table} ||= $dbh->prepare(' INSERT INTO '.$dbh->quote_identifier($table).' ( ItemID, Day, Revenue, Count ) VALUES ( ?, ?, ?, ? ) '); $sth->execute(@$_); }

        A variation on your first example did the trick for me. Still not exactly what I would have liked, but it gets the job done :)


      You could produce and prepare the specific queries as you need them. It would be easy and inefficient to use each statement once. Alternatively you could cache them and reuse them. A hash keyed on the table name to hold the prepared statements or a simple sub to prepare them combined with memoize.

      I solved this problem when I had to deal a few dozen tables all with the same format and function each named by a client number determined from input data. The trick is to use a synonym. Write code for one table using a false table name. Then create a synonym named with your false name. Then alter the synonym to point to the table you need to use when you need to and reuse the bulk of your code. If the term 'synonym' is not familiar, consider it an alias or a symbolyc link with its own name that represents another object (table?) that may have another name. I don't know the performance impact of this method but I suspect altering a synonym is pretty cheap next to recompiling your insert statements. Hope this helps.

        Alas, neither MySQL nor PostgreSQL (the 2 systems the OP mentions) support CREATE SYNONYM.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://836642]
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (5)
As of 2024-05-21 23:53 GMT
Find Nodes?
    Voting Booth?

    No recent polls found