goqu icon indicating copy to clipboard operation
goqu copied to clipboard

How to generate nested select statements

Open parnic opened this issue 2 years ago • 3 comments

Apologies if this is answered somewhere, I tried searching and couldn't find anything.

If I wanted to generate a nested select statement/select subquery similar to the following:

SELECT * FROM players WHERE team_id IN (SELECT id FROM teams WHERE season_id IN (SELECT id FROM seasons WHERE status='active'))

Is that possible with goqu? I've tried something like this for the nested bits:

seasonQuery := dialect.From(models.TableNames.Seasons).Select(models.SeasonColumns.ID).Where(goqu.C(models.SeasonColumns.Status).Eq(models.SeasonsStatusActive))
teamQuery := dialect.From(models.TableNames.Teams).Select(models.TeamColumns.ID).Where(goqu.C(models.TeamColumns.SeasonID).In(seasonQuery))

but using an SQLite dialect it ends up generating a query like:

SELECT `id` FROM `teams` WHERE (`season_id` IN ((SELECT `id` FROM `seasons` WHERE (`status` = 'active'))))

where the extra () in the IN breaks the select from seeing multiple IDs.

Is there a way to use one SelectDataset, or a derivative of it, in an In() like this?

parnic avatar Sep 06 '22 19:09 parnic

It looks like replacing In() with Eq() in the code above causes goqu to turn the Eq into an In internally and generate an appropriate query, so that works for me. I wouldn't have expected it to do that translation (especially when In() doesn't work the same way), so I guess consider this a feature request instead of a question.

Behaves as desired:

seasonQuery := dialect.From(models.TableNames.Seasons).Select(models.SeasonColumns.ID).Where(goqu.C(models.SeasonColumns.Status).Eq(models.SeasonsStatusActive))
teamQuery := dialect.From(models.TableNames.Teams).Select(models.TeamColumns.ID).Where(goqu.C(models.TeamColumns.SeasonID).Eq(seasonQuery))

=>

SELECT `id` FROM `teams` WHERE (`season_id` IN (SELECT `id` FROM `seasons` WHERE (`status` = 'active')))

parnic avatar Sep 06 '22 19:09 parnic

Hey @parnic, I've also recently stumbled upon this bug. I'll make the PR with a fix soon :) In short, In considers a subquery to be a list and that causes extra braces to be appended

nic11 avatar Sep 12 '22 18:09 nic11

@parnic I made a PR. But looks like replacing In with Eq gives the desired behavour. Not sure why is this like so and if it is a really the desired behaviour to treat In(Select(...)) like a 'in the array of one select', but looks very quirky

It even has this in tests:

expressionTestCase{val: ident.Eq(ae), sql: `("a" IN (SELECT "id" FROM "test2"))`},

PS. Noticed that you told the same thing about Eq/In just when I finished writing this comment :D

nic11 avatar Sep 22 '22 08:09 nic11