DEV Community

Cover image for Table subset searching with a list and DBIx::Class
Paul Cochrane 🇪🇺
Paul Cochrane 🇪🇺

Posted on • Originally published at peateasea.de on

Table subset searching with a list and DBIx::Class

When searching for a subset of rows in a database table by using a list of entries in one of its columns, one can use the WHERE column_name IN list syntax in an SQL query. How to do this using DBIx::Class wasn’t obvious to me; at least, not by reading the docs. I worked it out eventually. Here’s what I learned.

This won’t be new to many people, but it was new to me and I couldn’t find the part of the docs where this is discussed, so I thought I’d write it up here for my future self to find.

Subset selecting in SQL

Imagine this situation: you have a database table from which you want to select a subset of its rows depending upon known values of one of its columns. Take this data for example, which represents possible failure states in an application:

id name severity
0 Ok 0
1 Warning 1
2 Critical 2
3 Error 3
4 Degradation 1
5 Mismatch 1
6 Contamination 2
7 Unknown -1

Now imagine that not all parts of the application use all failure states. Some only need to use Ok, Warning, Critical, and Error, while others use Ok, Degradation, Mismatch and Unknown. How does one pull out the rows only of interest to that specific part of the application? One way to do this would be like so (note: I’m still a bit of an SQL noob, so be nice to me):

SELECT * FROM failure_states WHERE name IN ('Ok', 'Degradation', 'Mismatch', 'Unknown');
Enter fullscreen mode Exit fullscreen mode

That’s cool. Now, I’ve been working more with DBIx::Class recently, having spent the last several years working almost only with Django and its ORM, hence I’m also a DBIx::Class noob. Anyway, I wanted to do this lookup from DBIx::Class and only stumbled across the example mentioned in the search docs for DBIx::Class::ResultSet, i.e. this bit:

my @cds = $cd_rs->search({ year => 2001 }); # "... WHERE year = 2001"
my $new_rs = $cd_rs->search({ year => 2005 });

my $new_rs = $cd_rs->search([{ year => 2005 }, { year => 2004 }]);
               # year = 2005 OR year = 2004
Enter fullscreen mode Exit fullscreen mode

Using this pattern to solve the problem above yields code like this:

my @states = FailureState->search(
    [
        { name => 'Ok' },
        { name => 'Degradation' },
        { name => 'Mismatch' },
        { name => 'Unknown' },
    ]
);
Enter fullscreen mode Exit fullscreen mode

That, as one might say in the New Zealand vernacular, is fugling uckly. Putting things another way, there’s a lot of duplication here, which doesn’t make this solution DRY and it’s not a pattern that would scale well. It works, but surely there’s a better way, right?

Simpler subsets in DBIx::Class

It turns out that yes, there is a better way, and as is often the case, I found an appropriate answer on StackOverflow. Also, because this is Perl, there is more than one way to do it. For instance with the = operator within a search() method call:

my @state_names = qw(Ok Degradation Mismatch Unknown);
my @states = FailureState->search(
    {
        name => { '=' => [@state_names] }
    }
);
Enter fullscreen mode Exit fullscreen mode

This probably does something like WHERE name = $state_name in the background … I guess? I’m not sure about the details here, so I’m going to be content with waving my arms as an explanation.

Alternatively, one can use a syntax reminiscent of the WHERE column_name IN list syntax:

my @state_names = qw(Ok Degradation Mismatch Unknown);
my @states = FailureState->search(
    {
        name => { -in => [@state_names] }
    }
);
Enter fullscreen mode Exit fullscreen mode

But why use several lines when only two are sufficient? One can be more direct and pass an arrayref:

my @state_names = qw(Ok Degradation Mismatch Unknown);
my @states = FailureState->search( { name => \@state_names } );
Enter fullscreen mode Exit fullscreen mode

Nice!

Good enough for now

I’m sure there are other and better ways of doing this. Even so, this change has simplified my code nicely. Also, I learned something new today, which was cool :-)

Top comments (2)

Collapse
 
davorg profile image
Dave Cross

I couldn’t find the part of the docs where this is discussed

DBIC uses SQL::Abstract to build its SQL statements. So that's where this stuff is documented. Anyone working with DBIC should read the section on where clauses at least once.

Collapse
 
dragostrif profile image
DragosTrif

Isn"t DBix class abadoned? reddit.com/r/perl/comments/1eddi9n...