如何在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
相关产品推荐
相关产品推荐

