H2 用户定义的聚合函数 ListAgg 不能在第一个参数上使用 DISTINCT 或 TRIM()

Posted

技术标签:

【中文标题】H2 用户定义的聚合函数 ListAgg 不能在第一个参数上使用 DISTINCT 或 TRIM()【英文标题】:H2 User defined aggregate function, ListAgg, can't use DISTINCT or TRIM() on the first parameter 【发布时间】:2015-10-09 21:01:52 【问题描述】:

所以我有一个 DB2 生产数据库,我需要在其中使用可用的函数 ListAgg。我希望我的单元测试使用 H2 来正确测试这个功能。不幸的是,H2 不直接支持 ListAgg。但是,我可以创建一个用户定义的聚合函数...

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.h2.api.AggregateFunction;

import com.google.common.base.Joiner;

public class ListAgg implements AggregateFunction


private List<String> values = new ArrayList<String>();
private String delimiter = ",";

@Override
public void init(Connection conn) throws SQLException



@Override
public int getType(int[] inputTypes) throws SQLException

    if (inputTypes.length != 2) 
        throw new java.sql.SQLException("The aggregate function ListAgg must have 2 arguments.");
    
    return java.sql.Types.VARCHAR;


@Override
public void add(Object sqlValues) throws SQLException

    Object[] objects = (Object[]) sqlValues;
    this.delimiter = (String) objects[1];
    String value = (String) objects[0];
    values.add(value);


@Override
public Object getResult() throws SQLException

    return Joiner.on(delimiter).join(values);



我已经做到了。这适用于

ListAgg(columnName, ',')

但失败了

ListAgg(DISTINCT TRIM(columnName), ',')

我错过了什么?

编辑: 我收到以下错误消息:

org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback;
bad SQL grammar [select ..., LISTAGG(DISTINCT TRIM(columnName), ',') as 
columnName_LIST, from ... group by ...]; nested exception is 
org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "SELECT ... "; 
expected "NOT, EXISTS, INTERSECTS, SELECT, FROM"; SQL statement:
select ... from ... group by ... [42001-174]    
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:231) 
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)

EDIT2:

COUNT() 是否也实现了 AggregateFunction 还是在内部使用了其他东西?因为H2能够处理COUNT(DISTINCT columnName)

【问题讨论】:

从版本 1.4.198 (2019-02-22) 开始,H2 实现了 LISTAGG - 请参阅 changelog。它被描述为“部分”,但文档中的示例至少包括 LISTAGG(NAME, ', ') WITHIN GROUP (ORDER BY ID)LISTAGG(ID, ', ') WITHIN GROUP (ORDER BY ID) OVER (ORDER BY ID) 【参考方案1】:

我认为 H2 不允许您在自定义聚合函数上使用 DISTINCT 关键字。但是您可以轻松定义自己的“独特”版本的ListAgg

public class DistinctListAgg implements AggregateFunction 
    private Set<String> values = new LinkedHashSet<String>();
    // The rest is the same

【讨论】:

感谢您的宝贵时间。我很欣赏有关如何获得独特版本的见解。但我希望能够在不更改环境之间的代码的情况下进行测试。在这种情况下,我将在生产中运行 ListAgg(DISTINCT TRIM(columnName)),在单元测试中运行 [Distinct]ListAgg(columnName)。 嗯,DB2's LISTAGG 真的支持DISTINCT 吗?这是一个未记录的功能吗?无论如何,我怀疑当您不直接在 DB2 上进行集成测试时,您会一次又一次地遇到这些差异 LISTAGG 支持DISTINCT 真的那么令人惊讶吗? COUNTSUM 也是支持它的 (DB2) 聚合函数。 嗯,它没有记录,不幸的是,Oracle 的LISTAGG 不支持它。

以上是关于H2 用户定义的聚合函数 ListAgg 不能在第一个参数上使用 DISTINCT 或 TRIM()的主要内容,如果未能解决你的问题,请参考以下文章

oracle 聚合函数 LISTAGG ,将多行结果合并成一行

MySQL 中的聚合函数 - 列表(如 Oracle 中的 LISTAGG)

Listagg 函数与 PLSQL 集合

文本聚合函数(wm_concat, listagg, group_concat, string_agg)

SQL Server 的分组字符串聚合/LISTAGG

oracle的LISTAGG函数 和 REGEXP_SUBSTR函数的使用