Query aggregates
From ClassDBI
Sum of column
I needed a way to calculate the sum of a column.
The docs describe a way to do this when you don't need constraints:
my $sum = $class->sql_single("sum(my_column)")->select_val;
However, you often have to restrict this with certain criteria, so this is what I came up with. It uses SQL::Abstract to add the criteria.
First we set up the sql statement in our CDBI class:
__PACKAGE__->set_sql(sum_them => "SELECT SUM(%s) FROM __TABLE__ %s");
and then the core function:
sub sum {
my ($class) = shift;
my ($column) = shift;
my $sql = new SQL::Abstract;
my ($where, @bind) = $sql->where(@_);
return $class->sql_sum_them($column, $where)->select_val(@bind);
}
You'd call this as follows:
# no restrictions:
my $total = $class->sum('my_column');
# field1 must be 3:
my $sum_col = $class->sum('my_column', { field1 => 3 });
Now if SQL::Abstract supported "GROUP BY", then we could make this even more versatile... But this principle works great for what I needed to do.
-- RhesaRozendaal
Aggregates which don't set all of the primary key
Consider the following simple class with an aggregate search:
# Declare our primary key field(s) __PACKAGE__->columns(Primary => 'StaffID', 'ProjectName'); __PACKAGE__->columns(All => 'StaffID', 'ProjectName', 'Allocation'); __PACKAGE__->columns(TEMP => 'Total');
# Declare our relationships to the other tables/classes ...
# Select total time allocated to each project
__PACKAGE__->set_sql(total_times =>
"SELECT ProjectName, sum(Allocation) as Total
FROM Projects
GROUP BY ProjectName
ORDER BY ProjectName"
);
Since our aggregating function (search_total_times) isn't setting all of the primary keys, the following will fail to print any results:
my $results_it = search_total_times();
while(my $result = $results_it->next()) {
print Dumper $result;
}
This is because when a Class::DBI object is taken in boolean form it returns false if any part of its primary key is null. This is important because without a fully developed primary key the object cannot be a representation of a row in the database. As such changes to the object cannot be saved to the database.
A solution to make this work, in the understanding that search results of this form must be treated as if they were *read-only* is as follows:
my $results_it = search_total_times();
while( ref (my $result = $results_it->next()) ) {
print Dumper $result;
}
This asks instead whether $result was set to a reference, which it will be unless you've gone through all of your results.
The problem can also be avoided by using your results in a list format:
my @results = search_total_times();
foreach my $result (@results) {
print Dumper $result;
}
which will work as desired but shouldn't be used where your result set is large.
-- Jacinta Richardson

