joining on a subquery with dbix-class?
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.
精彩评论