简体   繁体   中英

Esqueleto: join on subquery (using subList_select)

I'm trying to translate the following SQL to Esqueleto:

SELECT id, task_id, author_id
FROM scenario
INNER JOIN ( SELECT task_id as tId, author_id as aId, MAX(last_update) as lastUp
             FROM scenario
             GROUP BY task_id, author_id
           ) t
      ON task_id = tId AND author_id = aId AND last_update = lastUp

To do a sub-query, you have to use subList_select .

I couldn't figure out a way to combine it with the pattern matching in:

from $ \(s `InnerJoin` ?subQueryhere?) -> do ...

So I tried with where_ instead:

where_ (s ^. ScenarioTaskId ==. (subList_select $
         from $ \s' -> do
         groupBy (s' ^. ScenarioTaskId, s' ^. ScenarioAuthorId)
         return s'
       ) ^. ScenarioTaskId)

However, this doesn't compile since subList_select returns a expr (ValueList a) instead of a expr (Entity Scenario) .

I was struggling with a similar thing.

You can use the 'experimental' module of the library (docs at https://hackage.haskell.org/package/esqueleto-3.4.2.2/docs/Database-Esqueleto-Experimental.html ) which is a bit more powerful than (and subtly different from) normal esqueleto.

The main differences are around from - you explicitely tell it what you are joining and there are quite a few options. You also need an extra extension turned on to use @TableName tags when you want to talk about tables.

I'd be tempted to rewrite everything into experimental except the errors that come out of it are trickier to sort out as you end up using more do notation.

Anyway here's an approximation of what you might need:

    (scenario :& (taskId, authorId) <- 
      from $ Table @Scenario `InnerJoin` SubQuery (do
        scenario <- from $ Table @Scenario   
        groupBy (scenario ^. ScenarioTaskId, scenario ^. ScenarioAuthorId)
        return (scenario ^. ScenarioTaskId, scenario ^. ScenarioAuthorId, max_(scenario ^. ScenarioLastUpdate))
        ) 
      `on` (\(scenario :& (taskId, authorId)) -> 
        (just (scenario ^. ScenarioTaskId) ==. just taskId) &&.
        (just (scenario ^. ScenarioAuthorId) ==. authorId) &&.
        (just (scenario ^. ScenarioLastUpdate) ==. lastUp)
      )
    return (scenario ^. ScenarioId, taskId, authorId)

Some adding/removing of just s in the on clause might be necessary. I found what I needed there very unintuitive.

Also be careful you use max_ (esqueleto) and not max (standard library) otherwise you'll have other confusing errors!

I guess I can use the following which should be semantically identical (inspired by this answer ):

$ select
$ from $ \( s `LeftOuterJoin` ms ) -> do
        on ( just (s ^. ScenarioAuthorId)  ==. ms ?. ScenarioAuthorId &&.
             just (s ^. ScenarioTaskId)    ==. ms ?. ScenarioTaskId   &&.
             just (s ^. ScenarioLastUpdate) <. ms ?. ScenarioLastUpdate )
  where_ (isNothing (ms ?. ScenarioId))

Still baffles me that Esqueleto apparently supports sub-queries with multiple results only in combination with in_ ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM