[Catalyst] Support distinct in search() and search_like()

Bill Moseley moseley at hank.org
Mon Nov 7 23:34:36 CET 2005


On Mon, Nov 07, 2005 at 09:44:12PM +0000, Pedro Melo wrote:
> hi,
> 
> On Nov 7, 2005, at 5:40 PM, Bill Moseley wrote:
> >On Mon, Nov 07, 2005 at 04:56:03PM +0000, Pedro Melo wrote:
> >>I needed Class::DBI::Sweet search() to use SELECT DISTINCT in one
> >>project I'm working on.
> >
> >In trunk there's an option to pass in the name of the sql method to
> >use instead of the default "Join_Retrieve".  With this you can define
> >your custom selects in your base class (or in one of your table's
> >classes) and tell Sweet to use that instead.
> 
> Looking at the C::D::Sweet code, is not clear to me what option is 
> that. The code seems hardwired to the name Join_Retrieve.

By "trunk" I mean in svn:

http://dev.catalyst.perl.org/file/trunk/Class-DBI-Sweet/

=item sql_method

This sets the name of the sql fragment to use as previously set by a
C<set_sql> call.  The default name is "Join_Retrieve" and the associated
default sql fragment set in this class is:

    __PACKAGE__->set_sql( Join_Retrieve => <<'SQL' );
    SELECT __ESSENTIAL(me)__%s
    FROM   %s
    WHERE  %s
    SQL

You may override this in your table or base class using the same name and CDBI::Sweet
will use your custom fragment, instead.

If you need to use more than one sql fragment in a given class you may create a new
sql fragment and then specify its name using the C<sql_method> attribute.

The %s strings are replaced by sql parts as described in L<Ima::DBI>.  See
"statement_order" for the sql part that replaces each instance of %s.

In addition, the associated statment for COUNT(*) statement has "_Count"
appended to the sql_method name.  Only "from" and "where" are passed to the sprintf
function.

The default sql fragment used for "Join_Retrieve" is:

    __PACKAGE__->set_sql( Join_Retrieve_Count => <<'SQL' );
    SELECT COUNT(*)
    FROM   %s
    WHERE  %s
    SQL

If you create a custom sql method (and set the C<sql_method> attribute) then
you will likely need to also create an associated _Count fragment.  If you do
not have an associated _Count, and wish to call the C<page> method,  then set
C<disable_sql_paging> to true and your result set from the select will be spliced
to return the page you request.

Here's an example.

Assume a CD has_a Artist (and thus Artists have_many CDs), and you wish to
return a list of artists and how many CDs each have:

In package MyDB::Artist

    __PACKAGE__->columns( TEMP => 'cd_count');

    __PACKAGE__->set_sql( 'count_by_cd', <<'');
        SELECT      __ESSENTIAL(me)__, COUNT(cds.cdid) as cd_count
        FROM        %s                  -- ("from")
        WHERE       %s                  -- ("where")
        GROUP BY    __ESSENTIAL(me)__
        %s %s                           -- ("limit" and "order_by")

Then in your application code:

    my ($pager, $iterator) = MyDB::Artist->page(
        {
            'cds.title'    => { '!=', undef },
        },
        {
            sql_method          => 'count_by_cd',
            statement_order     => [qw/ from where limit order_by / ],
            disable_sql_paging  => 1,
            order_by            => 'cd_count desc',
            rows                => 10,
            page                => 1,
        } );

The above generates the following SQL:

    SELECT      me.artistid, me.name, COUNT(cds.cdid) as cd_count
    FROM        artist me, cd cds
    WHERE       ( cds.title IS NOT NULL ) AND me.artistid = cds.artist
    GROUP BY    me.artistid, me.name
    ORDER BY    cd_count desc

The one caveat is that Sweet cannot figure out the has_many joins unless you
specify them in the $criteria.  In the previous example that's done by asking
for all cd titles that are not null (which should be all).

To fetch a list like above but limited to cds that were created before the year
2000, you might do:

    my ($pager, $iterator) = MyDB::Artist->page(
        {
            'cds.year'  => { '<', 2000 },
        },
        {
            sql_method          => 'count_by_cd',
            statement_order     => [qw/ from where limit order_by / ],
            disable_sql_paging  => 1,
            order_by            => 'cd_count desc',
            rows                => 10,
            page                => 1,
        } );



> 
> Line 280 of C::D::Sweet looks like this in the original code:
> 
>     my $sth = $class->sql_Join_Retrieve( $pre_fields, $from, $sql );
> 
> So the name is hardwired. I suppose I could override sql_Join_Retrieve 
> but it would use distinct on every query, not what I want.
> 
> Of course, if Sweet or C::DBI already has this, I would use it.
> 
> Thanks,
> --
> Pedro Melo
> JID: melo at simplicidade.org
> 
> 
> _______________________________________________
> Catalyst mailing list
> Catalyst at lists.rawmode.org
> http://lists.rawmode.org/mailman/listinfo/catalyst
> 

-- 
Bill Moseley
moseley at hank.org




More information about the Catalyst mailing list