gwhite has asked for the wisdom of the Perl Monks concerning the following question:

I have a set of scripts that get installed on various servers. I store the data using DBI, so I can support my clients various SQL servers. Up to this point, I have implemented configuration strings to set the SQL statements for each client to support slight differences in field names, or table names depending on client standard operating procedure, etc. While this works, it seems very unelegant and not the easiest to maintain.

For each of 5 different tables I insert new records, retrieve records by a user number (5-10 records max), retrieve records by a primary key or retrieve all records for summation. All the data is stored in an object so it is either going from the database to the object or the object to the database. My module functions are basically, db->save, db->get_user_recs, db->get_record, db->get_all_recs, and I pass the table_name and any parameter.

I am seeking suggestions on how my database module might be able to map the pieces of the user object back forth to the sql database and create decent sql without resorting to putting the raw sql statements into a configuration file, but still with the flexibility to add or subtract a field or tweak a field name.

g_White

Replies are listed 'Best First'.
Re: Creating a Flexible Database Module
by talexb (Chancellor) on Jan 28, 2002 at 19:39 UTC
    I'd suggest using a configuration module to store the various data elements that you need for each installation. That module could (on shaky ground here) export the required information about table names, field names, perhaps retrieving information from a hash of hashes.

    One call at the beginning would specify which installation you are using, and from then on the configuration module would return the appropriate names for that installation.

    --t. alex

    "Of course, you realize that this means war." -- Bugs Bunny.

Re: Creating a Flexible Database Module
by Biker (Priest) on Jan 28, 2002 at 20:24 UTC

    Consider hiding the technical differences behind an interface. Create a module with methods that have business related names, not technical names.

    For instance, name a method $sql->get_account_details() instead of $sql->select_act().

    Let this module work out where and how to find the data you're looking for and return a hashref or an arrayref, whichever is more appropriate.

    This way your main application will not know anything about the database structure, but just ask for (and hopefully get) business related information. Of course, the same goes for saving data. This way you can 'easily' add another database definition when using the software for a different client with a different db.

    Update: You should probably also consider naming the $sql something more generic, like $db or $storage just to avoid the assumption of an SQL database.

    "Livet är hårt" sa bonden.
    "Grymt" sa grisen...

Re: Creating a Flexible Database Module
by perrin (Chancellor) on Jan 28, 2002 at 20:28 UTC
    There are about a million modules out there for mapping DBI calls to objects. Some of the most popular include Class::DBI, SPOPS, Tangram, and Alzabo. Maybe one of those will meet your needs.
Re: Creating a Flexible Database Module
by trs80 (Priest) on Jan 28, 2002 at 20:34 UTC
    Setup a configuration file that contains all the valid field names for each database stored in hash or lists. This is some code I have been working on that may or may not help you, but has been very useful for my needs.
    my %table_fields = ( table1 => [ 'field1' , 'field2' , 'field3' ], table2 => [ 'field1' , 'field2' , 'field3' ], ); sub get_fields { my ($self,$table) = @_; return $table_fields{$table}; }
    Now you can pick through your current "keys" and see which ones match valid field names and pass only those to something like:
    sub save { my $self = shift; my $table = shift; my $args = shift; # this is your hash of fields and values my @place = (); my @field = (); my @value = (); # # insert into table # foreach (keys %{$args}) { push @place, '?'; push @field, $_; push @value, $args->{$_}; } my $string = qq[ insert into $table ( ] . join(' ,', @field) . qq[ ) values ( ] . join(' ,', @place) . qq[ ) ]; $self->error_to_log("$string"); my $id = $self->db_do($string , \@value ); return ($id); } sub db_do { my $self = shift; my $string = shift; my $placeholders = shift; my $id; $self->error_to_log("$string"); my $cursor = $self->dbh->prepare($string); $cursor->execute(@{ $placeholders }); if ($string =~ /^\s?insert/i) { $id = $self->dbh->{'mysql_insertid'}; $self->error_to_log("ID $id",1); } return ( $id ); }

      That looks like it is on the right track, I will probably adjust it to:

      my %table_fields = ( table1 => [ 'field1||objectname1' , 'field2||objectname2' , 'fiel +d3||objectname3' ], table2 => [ 'field1||objectname1' , 'field2||objectname8' , 'fie +ld3||objectname10' ], );

      Then I can split each element and have the user object that the data lives in right handy also. Good one, thanks

      g_White
Re: Creating a Flexible Database Module
by princepawn (Parson) on Jan 28, 2002 at 20:48 UTC
Re: Creating a Flexible Database Module
by Aristotle (Chancellor) on Jan 28, 2002 at 23:21 UTC
    A very elegant way might be storing your query strings in an own table of the database. Then you can for example SELECT query_string FROM queries WHERE action = 'store_product'. You might even use this approach to store different parts of the query string in different tables and use a join to parametrize your queries within a set of known ones. (This is an idea I picked up here on PM; see Re: Eliminating Dynamic SQL... for the original writeup.)

    Makeshifts last the longest.