On 8/30/06, Kurt Hansen <[EMAIL PROTECTED]> wrote: > I need to do sum, max, avg, group by on columns in my database. However, > I'm not sure the best way to do it in RDBO. The count() method in > Manager makes me think that there may be some pre-existing methods to do > these tasks. > > If not, is it best to create a method using QueryBuilder and Manager? > I'm guessing it would be something analogous to count(). Where can I > look to find an example of how the count method is built and integrated > into Manager?
The get_objects_count() method exists because it's a well-known, well-defined operation: tell me how many objects (from the primary table) would have been fetched had I called get_objects(). The get_objects() method always returns collections of "row objects" (objects derived from Rose::DB::Object), meaning that each thing returned is an object of that Manager's object_class. You can take each object returned by get_objects(), modify it, save(), re-load() it, and so on. Once you add an aggregate function like SUM() or AVG() to the query, however, the data returned from the database does not correspond to rows in a particular table. Instead, it's aggregate data derived from many table rows. Obviously, the things returned cannot be "row objects" and cannot be save()d and so on. I eventually plan to add a Manager method for constructing queries like this, but I'm still trying to decide exactly what it will return. In the meantime, the way to do it is with "canned" queries wrapped in custom Manager methods that you write yourself. You can do this totally from scratch, using plain DBI, perhaps also grabbing some metadata from row class (e.g., the table name below): package My::Product::Manager; ... sub get_average_prices_by_type { my($class) = shift; my $meta = My::Product->meta; my $sql = 'SELECT type, AVG(price) FROM ' . $meta->table . 'GROUP BY type'; my @rows; eval { my $dbh = My::DB->new->retain_dbh; local $dbh->{'RaiseError'} = 1; my $sth = $dbh->prepare($sql); $sth->execute; while(my $row = $sth->fetchrow_hashref) { push(@rows, $row); } $dbh->disconnect; }; die "Could not get average prices: $@" if($@); return wantarray ? @rows : [EMAIL PROTECTED]; } That's not very flexible, but it's totally under your control. (It also tends to be fast, since you're using DBI directly.) If you want to support customization of the query, you can use some undocumented features of the Manager to help build the SQL: package My::Product::Manager; ... sub get_average_prices { my($class, %args) = @_; # group_by is an undocumented Manager parameter that # only works with get_objects_sql() $args{'group_by'} ||= 'type'; $args{'select'} = "$args{'group_by'}, AVG(price)", my($sql, $bind) = $class->get_products_sql(%args); my @rows; eval { my $dbh = My::DB->new->retain_dbh; local $dbh->{'RaiseError'} = 1; my $sth = $dbh->prepare($sql); $sth->execute(@$bind); while(my $row = $sth->fetchrow_hashref) { push(@rows, $row); } $dbh->disconnect; }; die "Could not get average prices: $@" if($@); return wantarray ? @rows : [EMAIL PROTECTED]; } With this version of the method, you can take advantage of the built-in query parsing abilities: # Customize WHERE clause $rows = My::Product::Manager->get_average_prices( query => [ name => { like => '%foo%' } ]); You can also customize the grouping: # Group by something different $rows = My::Product::Manager->get_average_prices( query => [ name => { like => '%foo%' } ], group_by => 'type, in_stock', sort_by => 'type'); And so on. Again, this technique uses the undocumented Manager features. Those features probably won't go away until some "official" way to do the same thing appears, but no guarantees. Both of these techniques are not ideal, but if you hide the implementation in the Manager class, then your "end-user" code never has to know how these methods are implemented. When better support for this type of thing finally does arrive, you can swap out the implementation (perhaps adding adapter code to make the APIs match up) and you won't have to modify any code outside the Manager. (IME, Manager classes usually grow to contain a whole range of methods, from straight call-throughs to base class Manager methods, all the way down to entirely custom, vendor-specific SQL and database procedure calls. The point of having a separate Manager class is to hide the details of how each method is implemented and centralize the code for them.) Speaking of better official support for aggregate queries, if anyone has any ideas about what a generic API should return in lieu of row objects, I'm all ears. -John ------------------------------------------------------------------------- Using Tomcat but need to do more? Need to support web services, security? Get stuff done quickly with pre-integrated technology to make your job easier Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642 _______________________________________________ Rose-db-object mailing list Rose-db-object@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/rose-db-object