You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Haskell Esqueleto中实现MySQL的GROUP_CONCAT函数

在Esqueleto中实现MySQL适配的GROUP_CONCAT函数

语法差异说明

SQLite与MySQL的GROUP_CONCAT函数语法不兼容:

  • SQLite:参数直接逗号分隔,格式为GROUP_CONCAT(expr, delimiter)
  • MySQL:自定义分隔符必须通过SEPARATOR关键字声明,格式为GROUP_CONCAT(expr SEPARATOR delimiter)
    原SQLite版本使用unsafeSqlFunction构造调用,该函数会自动将参数列表以逗号拼接放入函数括号,生成的SQL不符合MySQL语法要求,执行结果不符合预期。

实现代码

推荐写法(Esqueleto 3.5+ 版本支持)

import Database.Esqueleto.Experimental (SqlExpr, Value, PersistField, unsafeSqlExpr)
import Data.Text (Text)

mysqlGroupConcat ::
     PersistField a
  => SqlExpr (Value a)  -- 待拼接的字段或表达式
  -> SqlExpr (Value Text) -- 分隔符值
  -> SqlExpr (Value Text)
mysqlGroupConcat expr delimiter = unsafeSqlExpr $
  "GROUP_CONCAT(" <> expr <> " SEPARATOR " <> delimiter <> ")"

兼容旧版本写法

如果使用的Esqueleto版本不支持SqlExpr的Monoid实例,可以通过内部SQL渲染接口实现:

{-# LANGUAGE OverloadedStrings #-}
import Database.Esqueleto.Internal.Internal (SqlExpr(..), unSqlExpr)
import Database.Esqueleto.Experimental (Value, PersistField)
import Data.Text (Text)

mysqlGroupConcat :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value Text) -> SqlExpr (Value Text)
mysqlGroupConcat expr delimiter = SqlExpr $ \conn info -> do
  exprSql <- unSqlExpr expr conn info
  delimSql <- unSqlExpr delimiter conn info
  pure $ "GROUP_CONCAT(" <> exprSql <> " SEPARATOR " <> delimSql <> ")"

使用方式

调用方式和原SQLite版本完全一致,例:

-- 生成SQL: GROUP_CONCAT(bookmark_tag.tag SEPARATOR ' ')
mysqlGroupConcat (t ^. BookmarkTagTag) (val " ")

额外说明

MySQLGROUP_CONCAT默认返回长度上限为1024字节,长文本拼接会被静默截断,需要拼接长内容时可在连接建立后执行会话级配置调整上限:

SET SESSION group_concat_max_len = 102400;

内容的提问来源于stack exchange,提问作者angie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 06:18:26