用 esqueleto 计算行数

Posted

技术标签:

【中文标题】用 esqueleto 计算行数【英文标题】:Counting rows with esqueleto 【发布时间】:2015-02-02 08:50:03 【问题描述】:

我正在尝试使用 Esqueleto(版本 2.1.2.1)计算内部连接的行数。不幸的是,我的代码无法编译,我不明白为什么。我查看了以下有关如何执行此操作的示例,但无法弄清楚我做错了什么:example1,example2。

我的架构如下所示(简化):

User
Game
  state
Player
  user UserId Maybe
  game GameId

用户可以在网站上注册玩游戏。无需注册也可以玩。因此,有一个单独的表Player。游戏有状态。它可以是Ongoing,或某种形式的游戏结束。我现在想统计用户正在玩的所有正在进行的游戏。

以下 SQL 查询可以做到这一点(对于 1 的固定 userId):

SELECT COUNT (*)
FROM Player INNER JOIN Game
ON Player.game = Game.id
WHERE Player.user = 1 AND game.state = "Ongoing"

但是,以下 Esqueleto 查询无法编译:

[count1] <- runDB $ E.select                                    -- Line 25
                  $ E.from $ \(player `E.InnerJoin` game) -> do
                      E.on $ player^.PlayerGame E.==. game^.GameId
                      E.where_ $
                          player^.PlayerUser E.==. E.just (E.val userId) E.&&.
                          game^.GameState E.==. E.val MyGame.Ongoing
                      return (game, player)
                      return E.countRows                        -- Line 32

错误信息如下所示:

Handler/ListUserGames.hs:25:23:
    No instance for (E.SqlSelect (expr0 (E.Value a0)) r0)
      arising from a use of ‘E.select’
    The type variables ‘r0’, ‘expr0’, ‘a0’ are ambiguous
    Note: there are several potential instances:
      instance (E.SqlSelect a ra, E.SqlSelect b rb) =>
               E.SqlSelect (a, b) (ra, rb)
        -- Defined in ‘Database.Esqueleto.Internal.Sql’
      instance (E.SqlSelect a ra, E.SqlSelect b rb, E.SqlSelect c rc) =>
               E.SqlSelect (a, b, c) (ra, rb, rc)
        -- Defined in ‘Database.Esqueleto.Internal.Sql’
      instance (E.SqlSelect a ra, E.SqlSelect b rb, E.SqlSelect c rc,
                E.SqlSelect d rd) =>
               E.SqlSelect (a, b, c, d) (ra, rb, rc, rd)
        -- Defined in ‘Database.Esqueleto.Internal.Sql’
      ...plus 13 others
    In the expression: E.select
    In the second argument of ‘($)’, namely
      ‘E.select
       $ E.from
         $ \ (player `E.InnerJoin` game)
             -> do  E.on $ player ^. PlayerGame E.==. game ^. GameId;
                     E.where_
                     $ player ^. PlayerUser E.==. E.just (E.val userId)
                       E.&&. game ^. GameState E.==. E.val MyGame.Ongoing;
                     .... ’
    In a stmt of a 'do' block:
      [count1] <- runDB
                  $ E.select
                    $ E.from
                      $ \ (player `E.InnerJoin` game)
                          -> do  E.on $ player ^. PlayerGame E.==. game ^. GameId;
                                  E.where_
                                  $ player ^. PlayerUser E.==. E.just (E.val userId)
                                    E.&&. game ^. GameState E.==. E.val MyGame.Ongoing;
                                  .... 

Handler/ListUserGames.hs:32:32:
    No instance for (E.Esqueleto query0 expr0 backend0)
      arising from a use of ‘E.countRows’
    The type variables ‘query0’, ‘expr0’, ‘backend0’ are ambiguous
    Note: there is a potential instance available:
      instance E.Esqueleto E.SqlQuery E.SqlExpr SqlBackend
        -- Defined in ‘Database.Esqueleto.Internal.Sql’
    In the first argument of ‘return’, namely ‘E.countRows’
    In a stmt of a 'do' block: return E.countRows
    In the expression:
      do  E.on $ player ^. PlayerGame E.==. game ^. GameId;
           E.where_
           $ player ^. PlayerUser E.==. E.just (E.val userId)
             E.&&. game ^. GameState E.==. E.val MyGame.Ongoing;
           return (game, player);
           return E.countRows 

但是,如果我删除 countRows,完全相同的查询会起作用。 IE。下面的代码编译并做我想做的事。

ongoing <- runDB $ E.select
                 $ E.from $ \(player `E.InnerJoin` game) -> do
                     E.on $ player^.PlayerGame E.==. game^.GameId
                     E.where_ $
                       player^.PlayerUser E.==. E.just (E.val userId) E.&&.
                       game^.GameState E.==. E.val MyGame.Ongoing
                     E.orderBy [E.desc $ game^.GameLastActionTime]
                     E.limit pagelen
                     E.offset $ max 0 $ (page1 - 1) * pagelen
                     return (game, player)

我做错了什么?

【问题讨论】:

【参考方案1】:

事实证明,上面的 Esqueleto 代码是正确的。错误出现在代码的另一部分,由于缺乏限制导致类型模糊。

【讨论】:

什么是类型歧义?我遇到了同样的问题,但不知道在哪里添加额外的限制 @Agumander 恐怕已经很久了,我不记得到底是什么问题。但是,现在查看代码,似乎在模式绑定中将count1 更改为E.Value count1 应该会有所帮助。 这导致了突破!在我的情况下,我还指定了 [E.Value (count1 :: Int)],因为它后来被传递给显示。谢谢!

以上是关于用 esqueleto 计算行数的主要内容,如果未能解决你的问题,请参考以下文章

linux wc指令(wc命令)(Word count)(统计文件的字节数单词数行数等信息)

linux wc指令(wc命令)(Word count)(统计文件的字节数单词数行数等信息)

单词统计:对程序设计语言源文件统计字符数单词数行数,统计结果以指定格式输出到默认文件中

python脚本: 双向统计文件字符单词数行数

Android 简单统计文本文件字符数单词数行数Demo

WordCount--统计输入文件的字符数行数单词数(java)--初级功能