geektron has asked for the wisdom of the Perl Monks concerning the following question:
i started this writeup earlier when i was at work ... but couldn't get it finished ( the lull in the day came to an abrupt end ).
i've been looking into easier, faster ways of generating some of my SQL queries. ( faster on the coding time, at least ). i've poked around a bit in the Class::DBI perldoc, but it looks like using it for more complicated joins ( like LEFT JOINs when they're needed ) would be almost more of a hassle than a help ...
the first place i've been looking to replace some of my SQL is actually in my INSERT/UPDATE queries. i tend to do this a lot:
sub storeData {
my $self = shift;
my @fields = qw/ foo bar baz quid quux /;
my $insStmt = "INSERT INTO table
VALUES ( " ;
$insStmt .= "? " x ( scalar( @fields ) +1 );
$insStmt .= " ) ";
my $updStmt = "UPDATE table
SET " . join( "$_ = ?, ", @fields;
$updStmt .= "WHERE id = ? ";
$useSql = ( $id ? $updStmt : $insStmt );
then run through all the usual prepare, execute, bind_params, etc. as needed ( working off the @fields array.
it's fairly maintainable. adding an element to @fields gets the column added to the ins/upd statements, but I'm starting to wonder how much easier it could get.
would Class::DBI be an appropriate solution?
Re: 'automating' SQL queries, use Class::DBI?
by blokhead (Monsignor) on Jan 27, 2004 at 07:26 UTC
|
Class::DBI isn't really for abstracting the SQL-generation process. Of course, it must generate SQL, but everything's under the hood -- you don't use Class::DBI to get back SQL that you execute yourself. Use it (or similar modules*) when you want to have an abstract, object-oriented view of your data and not have to think about SQL at all. The catch for this abstraction is that it's harder to do complex queries all at once. But don't think you can't get the same results, for instance:
for my $cd (@cds) {
print $cd->name, $/;
printf " - %d: %s\n", $_->num, $_->song->name
for sort { $a->num <=> $b->num } $cd->tracks;
}
This code simulates a join among the "cds", "tracks", and "songs" tables using an object-persistence interface. You get the same results. Of course, it uses many queries on the database instead of just one join query. You need to weigh the gains of this abstraction against the performance of your complex queries when you simulate them like this. But also keep in mind that Class::DBI and friends execute most select queries using the primary key as a constraint, making all those small individual queries very fast.
OK, now assuming you really do want to abstract the SQL-generation process yourself, to do the whole generate-SQL/prepare/execute/fetch process, then here are some good starting points for common queries. Personally, I prefer using sprintf for SQL generation:
my $select = sprintf "select %s from $table where %s",
join("," => @cols),
join(" and " => map { "$_=?" } keys %constraints);
my $sth = $dbh->prepare($select);
$sth->execute( values %constraints );
#########
my $insert = sprintf "insert into $table (%s) values (%s)",
join("," => keys %data),
join("," => ('?') x scalar keys %data);
my $sth = $dbh->prepare($insert);
$sth->execute( values %data );
##########
my $update = sprintf "update $table set %s where %s",
join("," => map { "$_=?" } keys %data),
join(" and " => map { "$_=?" } keys %constraints);
my $sth = $dbh->prepare($update);
$sth->execute( values %data, values %constraints );
But if you do need to do complex stuff like joins, you'd probably be better off with a CPAN solution. You're own your own there, I've never tried any of them myself. Try searching for SQL or in the DBIx namespace.
*: similar modules = Class::Tables. I'm quite biased, but I can't pass up any opportunity for a plug ;)
| [reply] [d/l] [select] |
|
my $cnt = 1;
foreach my $field ( @fields ) {
$sth->bind_param( $cnt, $q->param( $field );
$cnt++;
}
i'll probably end up poking around more, but since i'm the only developer there, i don't want to blow too much time looking for solutions that don't really result in a gain. | [reply] [d/l] [select] |
Re: 'automating' SQL queries, use Class::DBI?
by edoc (Chaplain) on Jan 27, 2004 at 08:14 UTC
|
Class::DBI takes care of all the basics for you while at the same time helping to keep all your sql stuff in dedicated modules. Once you have your module set up for a table inserts & updates are easy as pie and custom queries aren't much harder.
package My::Table;
use base 'My::Class::DBI';
__PACKAGE__->table('blah');
__PACKAGE__->columns( All => qw/ foo bar baz quid quux / );
__PACKAGE__->columns( Essential => qw/ foo bar / );
__PACKAGE__->columns( Primary => 'foo' );
# assuming we have another table called 'other'..
# this creates a method called sql_method_name
__PACKAGE__->set_sql( method_name => qq( select b.foo, b.bar, o.summat
+, o.summat_else from blah b left join other o using(foo) where b.quid
+=?));
sub public_method_name {
my ($class,$quid) = @_;
my $sth = $class->sql_method_name();
$sth->execute($quid);
return @{$sth->fetchall_arrayref({})};
}
and using the module...
# fetch a My::Table object
my $record = My::Table->create({ foo => 1,
bar => 'bar val',
baz => 'baz val',
quid => 'quid val',
quux => 'quux val' });
# update a value
$record->quid('new quid val');
$record->update;
# fetch a list of records via our join method
my @list = My::Table->public_method_name('new quid val');
I've also recently started using TEMP columns to store values from other tables in the retrieved object via custom join methods so I can still use the oo interface to read the values (can't update in this case) but it's beer o'clock and I don't have an example of that handy.. 8)
| [reply] [d/l] [select] |
|
Class::DBI takes care of all the basics for you while at the same time helping to keep all your sql stuff in dedicated modules. Once you have your module set up for a table inserts & updates are easy as pie and custom queries aren't much harder.
i already do what i can to pull all of my SQL out of main 'functional' modules and put them into SQL.pm-type classes/packages ( i think the current one is named ArticleQureies.pm and all it does is fetch/store and return results ).
i'll have to spend some time playing with it, but it doesn't seem like much of a win to write the query, install it as a method, and then call the method. ( well, OK, it does, but it doesn't require the 'overhead' of importing another module. )
| [reply] |
Re: 'automating' SQL queries, use Class::DBI?
by perrin (Chancellor) on Jan 27, 2004 at 17:07 UTC
|
There is nothing that will do a good job of generating SQL for complicated outer joins and the like. Class::DBI does a good job of mapping tables to objects and letting you address your data in an OO way. It can generate SQL for simple things like what you show above, and it can take arbitrary SQL and handle to busy-work of fetching the data and turning it into objects that you can work with. | [reply] |
|
i don't expect there to be a good way of generating everything should of a lot of concatenation ... :-(
and i'm not particularly fond of table --> object mapping ( if it's a one-to-one relationship ). i've already had to deal with a 'home-grown' solution with a previous job that just became unmaintainable, unweildy, slow, and excessively complicated.
| [reply] |
|
Class::DBI is better than most homegrown versions of this sort of application, which is why people use it.
| [reply] |
Re: 'automating' SQL queries, use Class::DBI?
by Anonymous Monk on Jan 27, 2004 at 07:54 UTC
|
perldoc Class::DBI
...
INTRODUCTION
Class::DBI provides a convenient abstraction layer to a database.
It not only provides a simple database to object mapping layer, but can
be used to implement several higher order database functions (triggers,
referential integrity, cascading delete etc.), at the application level,
rather than at the database.
| [reply] |
|
|