【问题标题】: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 上)。

【问题讨论】:

    标签: java sql jooq


    【解决方案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 的人,尽管我将那里的示例简化为单层嵌套而犯了错误。我提出这个只是为了指出使用横向连接的解决方法,但是,正如你所说,这种方法最终需要根据了解实现来提取值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 2020-06-28
    • 2013-01-08
    • 2013-05-03
    相关资源
    最近更新 更多