如何以模块化方式构成Database.Esqueleto查询,以便在定义“基本”查询和相应的结果集之后,可以通过添加其他内部联接和where表达式来限制结果集。
另外,由于基本查询不是照此执行的,而是使用LIMIT和OFFSET的修改版本,因此如何将返回实体列表(或字段元组)的基本查询转换为对结果集进行计数的查询。
Yesod书中采用的以下不正确的Haskell代码段有望阐明我的目标。
{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-} {-# LANGUAGE GADTs, FlexibleContexts #-} import qualified Database.Persist as P import qualified Database.Persist.Sqlite as PS import Database.Persist.TH import Control.Monad.IO.Class (liftIO) import Data.Conduit import Control.Monad.Logger import Database.Esqueleto import Control.Applicative share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| Person name String age Int Maybe deriving Show BlogPost title String authorId PersonId deriving Show Comment comment String blogPostId BlogPostId |] main :: IO () main = runStdoutLoggingT $ runResourceT $ PS.withSqliteConn ":memory:" $ PS.runSqlConn $ do runMigration migrateAll johnId <- P.insert $ Person "John Doe" $ Just 35 janeId <- P.insert $ Person "Jane Doe" Nothing jackId <- P.insert $ Person "Jack Black" $ Just 45 jillId <- P.insert $ Person "Jill Black" Nothing blogPostId <- P.insert $ BlogPost "My fr1st p0st" johnId P.insert $ BlogPost "One more for good measure" johnId P.insert $ BlogPost "Jane's" janeId P.insert $聽Comment "great!" blogPostId let baseQuery = select $ from $ \(p `InnerJoin` b) -> do聽 on (p ^. PersonId ==. b ^. BlogPostAuthorId) where_ (p ^. PersonName `like` (val "J%")) return (p,b) -- Does not compile let baseQueryLimited = (,) <$> baseQuery <*> (limit 2) -- Does not compile let countingQuery = (,) <$> baseQuery <*> (return countRows) -- Results in invalid SQL let commentsQuery = (,) <$> baseQuery <*> (select $聽from $ \(b `InnerJoin` c) -> do on (b ^. BlogPostId ==. c ^. CommentBlogPostId) return ()) somePosts <- baseQueryLimited count <- countingQuery withComments <- commentsQuery liftIO $ print somePosts liftIO $ print ((head count) :: Value Int) liftIO $ print withComments return ()
对于LIMIT和COUNT,hammar的答案是完全正确的,因此我不会深入研究它们。我只是重申一下,一旦使用,select您将无法再次以任何方式更改查询。
LIMIT
COUNT
select
对于JOINs,当前您无法INNER JOIN使用在其他from(nor (FULL|LEFT|RIGHT) OUTER JOIN)中定义的查询执行。但是,您 可以 执行隐式联接。例如,如果您已定义:
JOIN
INNER JOIN
from
(FULL|LEFT|RIGHT) OUTER JOIN
baseQuery = from $ \(p `InnerJoin` b) -> do on (p ^. PersonId ==. b ^. BlogPostAuthorId) where_ (p ^. PersonName `like` val "J%") return (p, b)
然后您可能会说:
commentsQuery = from $ \c -> do (p, b) <- baseQuery where_ (b ^. BlogPostId ==. c ^. CommentBlogPostId) return (p, b, c)
然后,Esqueleto会生成以下内容:
SELECT ... FROM Comment, Person INNER JOIN BlogPost ON Person.id = BlogPost.authorId WHERE Person.name LIKE "J%" AND BlogPost.id = Comment.blogPostId
不漂亮,但可以完成INNER JOINs的工作。如果需要执行a,OUTER JOIN则必须重构代码,以使所有OUTERJOINs都相同from(请注意,您可以在OUTER JOINs之间进行隐式联接就可以了)。
OUTER JOIN
OUTERJOIN