开发者

joining on a subquery with dbix-class?

开发者 https://www.devze.com 2023-04-13 01:42 出处:网络
Given two tables: (incoming greatly reduced/simplified example that exhibits the key problem) app_data represents applications that can be subscribed to

Given two tables: (incoming greatly reduced/simplified example that exhibits the key problem)

app_data represents applications that can be subscribed to

id   app_name
 1   apple
 2   berry
 3   cherry

app_sub maps email addresses to applications

id   email
 1   alex
 2   bob
 2   coby

I'd like to generate a table from a single-user point-of-view showing which applications the current user is subscribed to and not.

For example, from Alex's perspective I'd like to get:

desired_table

id    app_name    is_subscribed
 1    apple       true
 2    berry       false
 3    cherry      false

The following pure SQL query seems to be fine:

select id, app_name, email
  from app_data left join ( select *
                              from app_sub
                             where email='alex'
                          ) as subquery
                          on app_name.id=app_data.id;

However I'm having great difficulty getting that to work in dbix-class.

Alternatively, I tried to eliminate the subquery like so:

$app_data_resultset->search( { -or => [ { email => 'alex' },
                                        { email => undef },
                                      ],
                             },
                             { select => [ qw{ me.id
                                               me.app_name
                                               app_sub.email
                                             },
                                         ],
                               as => [ qw{ id
 开发者_StackOverflow                                          app_name
                                           email
                                         },
                                     ],
                               join => 'app_sub',
                           );

However, this (now expectedly) results in the following (after treating both 0 and null as false):

bad_table

id    app_name    is_subscribed
 1    apple       true
 3    cherry      false

Since 'bob' and 'coby' are subscribed to id 2, the where clause completely eliminates the second id.

Any help would be greatly appreciated!


I did this myself without going 100% Arbitrary SQL because IMO that kinda sucks. What I did instead was insert said subqueries into some raw SQL, and then use that to generate more queries. So here's my example (from https://github.com/frioux/drinkup/blob/master/lib/DU/Schema/ResultSet/Drink.pm#L164):

sub ineq {
   my ($self, $ingredient_rs, $min, $max) = @_;

   my $ingredients_on_hand = $ingredient_rs
      ->search(undef, {
         join => { kind_of => 'links_to_drink_ingredients' },
         columns => {
            drink_id => 'links_to_drink_ingredients.drink_id',
            ingredient_count => { count => '*', -as => 'ingredient_count' },
         },
         group_by => 'links_to_drink_ingredients.drink_id',
      })->as_query;


   my $required_ingredients = $self->result_source->schema->resultset('Drink_Ingredient')
      ->search(undef, {
         columns => {
            drink_id => 'me.drink_id',
            ingredient_count => { count => '*', -as => 'ingredient_count' },
         },
         group_by => 'me.drink_id',
      })->as_query;

   my ($ioh_sql, @ioh_bind) = @{$$ingredients_on_hand};
   my ($ri_sql, @ri_bind) = @{$$required_ingredients};

   my $creation = \[
   <<"SQL",
      SELECT di.drink_id FROM (
         $ri_sql di,
         $ioh_sql ii
      )
      WHERE di.drink_id = ii.drink_id AND
            di.ingredient_count >= ii.ingredient_count + ? AND
            di.ingredient_count <= ii.ingredient_count + ?
SQL
   @ri_bind, @ioh_bind, [{ sqlt_datatype => 'int' } => $min], [{ sqlt_datatype => 'int' } => $max] ];

   $self->search({ 'me.id' => { -in => $creation } });
}

If I get some time at some point I'll winnow this down to a more digestible example, but the point is that I use DBIx::Class to generate the hard queries and manually smash them together in one small spot, and then wrap THAT in a subquery so that I can just use plain DBIx::Class resultset methods for more searches.


Vincent, I am trying to join to a subquery now myself. I cannot find anyone succeeding so far.

An alternative is to just do it in plain SQL with $c->model('blah')->storage->dbh->prepare("query") OR the 'Arbitrary SQL through a custom ResultSource' section of the DBIx Class Cookbook, which also seems like a reasonable way.

0

精彩评论

暂无评论...
验证码 换一张
取 消