如何使用 Esqueleto 执行“SELECT ... IN (SELECT ...)”?

Posted

技术标签:

【中文标题】如何使用 Esqueleto 执行“SELECT ... IN (SELECT ...)”?【英文标题】:How to do a "SELECT ... IN (SELECT ...)" using Esqueleto? 【发布时间】:2017-05-28 19:35:27 【问题描述】:

考虑以下两个模型和一个GET /articles/:slug/comments请求,我想根据它的slug检索属于一篇文章的cmets。

Article json sql=articles
    slug        Slug
    title       Text
    description Text
    body        Text
    createdAt   UTCTime default=now()
    updatedAt   UTCTime Maybe default=NULL
    userId      UserId
    UniqueSlug  slug

Comment json sql=comments
    body      Text
    createdAt UTCTime default=now()
    updatedAt UTCTime Maybe default=NULL
    articleId ArticleId
    userId    UserId

使用持久化的rawSql,我们可以如下实现

getCommentsForArticle :: Slug -> App (Cmts [Entity Comment])
getCommentsForArticle slug = do
    comments <- runDb $ rawSql stm [toPersistValue slug]
    return (Cmts comments)
        where stm = "SELECT ?? FROM comments \
                    \WHERE article_id IN (\
                        \SELECT id FROM articles WHERE slug = ?)"

但是,鉴于我想维护 Haskell 和 SQL 之间的类型安全,我想使用 esqueleto 重写它。这是我正在努力的部分。通过阅读文档,sub_select 似乎是完成这项工作的工具。这是我所拥有的:

getCommentsForArticle :: Slug -> App (Cmts [Comment])
getCommentsForArticle slug = do
    comments <- E.select $
        E.from $ \cmts -> do
            let subQuery =
                    E.from $ \arts -> do
                        E.where_ $ arts ^. ArticleSlug ==. E.val slug
                        return (arts ^. ArticleId)
            E.where_ $ cmts ^. CommentArticleId ==. E.sub_select subQuery
            return cmts
    return $ Cmts comments

我也注意到in_ operator,但我不知道如何使用它,也不知道它是否比sub_select 更合适。

我错过了什么?语法是否正确?谢谢。

【问题讨论】:

不相关,但SELECT * ... 风格不好:)。为什么不直接使用 SQLite 或任何其他 SQL DB?它们的类型都很好;) @Igor 糟糕的风格是指不只选择所需的内容?根据您的第二个问题,我已经编辑了问题以指定我使用persistentpostgresql @Igor Esqueleto 旨在维护 Haskell 和 SQL 之间的类型安全。如果您使用纯 SQL,编译器不能也不会检查类型是否在两个世界之间正确编组。 @Igor,@chi 说的就是我要转esqueleto的原因。 无论如何,快速浏览一下文档:sub_select 只返回 one 值,因此它看起来是错误的工具。我会尝试 in_subList_select 来紧密匹配 SQL。 【参考方案1】:

你会想要这样的东西

getCommentsForArticle slug = do
  c <- select $ from $ \cmts -> do
         let a = subList_select $ from $ \arts -> do
                 where_ $ arts ^. ArticleSlug ==. val slug
                 return $ arts ^. ArticleId
         where_ $ cmts ^. CommentArticleId `in_` a
         return cmts
  return $ Cmts c

【讨论】:

需要在subList_select from 中有一个$,例如subList_select $ from,否则将失败。请修复

以上是关于如何使用 Esqueleto 执行“SELECT ... IN (SELECT ...)”?的主要内容,如果未能解决你的问题,请参考以下文章

Esqueleto:如何使用联接删除项目

简化 Persistent & Esqueleto 代码

使用 Esqueleto 处理列表类型

尝试使用我的模型设置 Esqueleto 时出错

在 Esqueleto 中获取聚合函数的结果

在 Esqueleto 中更新具有特定 ID 的行