[Dbix-class] Left join with an extra condition

Bill Moseley moseley at hank.org
Mon Oct 26 00:02:01 GMT 2009


Hi Peter,

Thanks for helping.

On Sun, Oct 25, 2009 at 6:54 AM, Peter Rabbitson
<rabbit+dbic at rabbit.us<rabbit%2Bdbic at rabbit.us>
> wrote:


> 2) You can take a resultset from your original source, and *temporarily*
> (i.e.
> just for the life of this particular resultset object) substitute the
> 'from'
> attribute with a scalarref of the SQL you want to execute. The 'from'
> attribute represents everything between the FROM and WHERE keywords. There
> used to be documentation of this attribute, but it was mostly factually
> incorrect and thus was undocumented. However you might see how the
> scalarref
> part worked here (the rest is now mostly irrelevant, as the format has
> changed,
> A LOT): http://dev.catalyst.perl.org/svnweb/bast/revision/?rev=3D7711
>

This seems to be the best solution for this specific problem.

I have the typical "music" database, with an extra table that tracks "gigs"
and the venue where those gigs are:

 select * from artist;
 id |     name     | label
----+--------------+-------
  1 | artist one   |     1
  2 | artist two   |     1
  3 | artist three |     2
  4 | artist four  |     2


select * from gig order by artist;
 id | artist |  venue
----+--------+---------
  1 |      1 | outside
  2 |      1 | outside
  3 |      1 | outside
  4 |      1 | inside
  5 |      2 | outside
  6 |      2 | outside
  7 |      2 | inside
  8 |      2 | inside
  9 |      3 | studio

And I want a count of how many "outside" gigs each artist plays:

    SELECT
        a.id, a.name,
        count(g.id) as gig_count
    FROM
        artist a
        LEFT JOIN gig g on g.artist =3D a.id AND g.venue =3D 'outside'
    GROUP BY
        1,2
    ORDER BY a.id
 id |     name     | gig_count
----+--------------+-----------
  1 | artist one   |         3
  2 | artist two   |         2
  3 | artist three |         0
  4 | artist four  |         0

So, the custom ResultSet::Artist method is:

sub outside_gigs {
    my $rs =3D shift;

    return $rs->search( undef,
        {
            'select' =3D> [
                qw/ me.id me.name /,
                {
                    count =3D> 'gigs.id',
                    -as   =3D> 'gig_count',
                },
            ],
            from =3D> \q{
                artist me LEFT JOIN gig gigs
                    ON me.id =3D gigs.artist
                    AND gigs.venue =3D 'outside'
            },

            as =3D> [qw/ id name gig_count / ],
            group_by =3D> '1,2',
        },
    );

}

What's good about this is the custom method is abstracted out into the
Artist's resultset class.  That is, the implementation of outside_gigs() is
hidden.  Plus, can make use of the ORM's features to limit rows and fetch a
given page:

my @artists =3D $schema->resultset('Artist')->search(
    undef,
    {
        rows =3D> 2,
        page =3D> 2,
        order_by =3D> 'id',
    },
)->outside_gigs->all;

With the limitation that can't use a "join" or "prefetch".  Plus, not sure
that will support a more complex query (e.g.  where there's bind parameters
in a CASE in the SELECT list).



-- =

Bill Moseley
moseley at hank.org
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/dbix-class/attachments/20091025/c4e=
4fba8/attachment.htm


More information about the DBIx-Class mailing list