如何按嵌套多重集中的值排序?

Posted

技术标签:

【中文标题】如何按嵌套多重集中的值排序?【英文标题】:How to order by values from nested multisets? 【发布时间】:2021-12-03 05:36:25 【问题描述】:

earlier question 介绍了如何使用 jOOQ 的 MULTISET_AGG 功能在嵌套集合上基于聚合函数对查询进行排序:使用 GROUP BY 按父表中的所有选定列对查询进行分组,而不是试图利用MULTISET 的实现细节。

是否有适用于多级集合的方法?我正在使用 PostgreSQL,但显然如果有一种独立于引擎的方式来做到这一点,那就更好了。

例如,如果我有一个使用 MULTISET 的嵌套查询:

var results =
    create
        .select(
            ORGANIZATIONS.ID,
            ORGANIZATIONS.NAME,
            multiset(
                    select(
                        DEPARTMENTS.ID,
                        DEPARTMENTS.NAME,
                        multiset(
                            select(EMPLOYEES.ID, EMPLOYEES.SALARY, EMPLOYEES.NAME)
                                .from(EMPLOYEES)
                                .where(EMPLOYEES.DEPARTMENT_ID.eq(DEPARTMENTS.ID)))))
                .from(DEPARTMENTS)
                .where(DEPARTMENTS.ORGANIZATION_ID.eq(ORGANIZATIONS.ID)))
        .from(ORGANIZATIONS)
        .fetch();

如果我在最里面的查询中输入ORDER BY,我可以轻松地按工资对每个部门的员工进行排序。

                        multiset(
                            select(EMPLOYEES.ID, EMPLOYEES.SALARY, EMPLOYEES.NAME)
                                .from(EMPLOYEES)
                                .where(EMPLOYEES.DEPARTMENT_ID.eq(DEPARTMENTS.ID))
                                .orderBy(EMPLOYEES.SALARY.desc()))))

但是我如何也可以按最高员工薪水对部门列表进行排序,以及按跨部门最高员工薪水对组织列表进行排序?

MULTISETs 更改为MULTISET_AGGs 并且只在查询的顶层执行ORDER BY,就像在前面的问题中只有一层嵌套一样,似乎在这里不起作用,因为您需要同时按两组不同的列进行分组,而且还因为您不能嵌套聚合函数(至少在 PostgreSQL 上)。

【问题讨论】:

【参考方案1】:

如果不访问某些表两次,我想不出一种简单的方法来汇总这些总和。如果我这样做,我会更新这个答案。但这里有一个解决方案可以回答您的问题:

var results = create
    .select(
        ORGANIZATIONS.ID,
        ORGANIZATIONS.NAME,
        multiset(
            select(
                DEPARTMENTS.ID,
                DEPARTMENTS.NAME,

                // Employees per department sorted by salary
                multisetAgg(EMPLOYEES.ID, EMPLOYEES.SALARY, EMPLOYEES.NAME)
                    .orderBy(EMPLOYEES.SALARY.desc()))
           .from(DEPARTMENTS)
           .leftJoin(EMPLOYEES)
               .on(EMPLOYEES.DEPARTMENT_ID.eq(DEPARTMENTS.ID))
           .where(DEPARTMENTS.ORGANIZATION_ID.eq(ORGANIZATIONS.ID))
           .groupBy(DEPARTMENTS.ID, DEPARTMENTS.NAME)

           // Departments sorted by max employee salary
           .orderBy(max(EMPLOYEES.SALARY).desc()))
    .from(ORGANIZATIONS)

    // Organisations sorted by max employee salary via extra join
    .orderBy(field(
        select(max(EMPLOYEES.SALARY))
        .from(EMPLOYEES)
        .where(EMPLOYEES.departments().ORGANIZATION_ID.eq(ORGANIZATION.ID))
    ).desc())
    .fetch();

另一种解决方案是仅在查询后对外部结果进行排序:

var results = create
    .select(
        ORGANIZATIONS.ID,
        ORGANIZATIONS.NAME,
        multiset(
            select(
                DEPARTMENTS.ID,
                DEPARTMENTS.NAME,
                multisetAgg(EMPLOYEES.ID, EMPLOYEES.SALARY, EMPLOYEES.NAME)
                    .orderBy(EMPLOYEES.SALARY.desc()),
                max(EMPLOYEES.SALARY))
           .from(DEPARTMENTS)
           .leftJoin(EMPLOYEES)
               .on(EMPLOYEES.DEPARTMENT_ID.eq(DEPARTMENTS.ID))
           .where(DEPARTMENTS.ORGANIZATION_ID.eq(ORGANIZATIONS.ID))
           .groupBy(DEPARTMENTS.ID, DEPARTMENTS.NAME)
           .orderBy(max(EMPLOYEES.SALARY).desc()))
    .from(ORGANIZATIONS)
    .fetch()
    .sortDesc(Comparator.comparing(
        // r.value3() is the outer multiset()
        r -> r.value3().isEmpty() 
           ? 0 

        // r.value3().get(0).value4() is the top max(EMPLOYEES.SALARY) value
           : r.value3().get(0).value4()
    ));

可以定义一个新的运算符来再次从MULTISET 中提取一个值,而不依赖于它的 SQL/XML 或 SQL/JSON 序列化实现。或者,您可以使用plain SQL templating 自行开发,知道序列化实现是什么样的(知道它将来可能会发生微妙的变化)

【讨论】:

谢谢!就其价值而言,我是提交this GitHub issue 的人,尽管我将那里的示例简化为单层嵌套而犯了错误。我提出这个只是为了指出使用横向连接的解决方法,但是,正如你所说,这种方法最终需要根据了解实现来提取值。

以上是关于如何按嵌套多重集中的值排序?的主要内容,如果未能解决你的问题,请参考以下文章

Falcor 查询中的多重嵌套

使用 Mongoose.js 按嵌套数组中的字段排序

嵌套设置中的混合效应模型或多重回归比较

如何按嵌套在其中的元素对 PHP 数组进行排序?

按嵌套字典值排序?

如何在 Python 中对字典列表进行多重排序? [复制]