如何按嵌套多重集中的值排序?
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()))))
但是我如何也可以按最高员工薪水对部门列表进行排序,以及按跨部门最高员工薪水对组织列表进行排序?
将MULTISET
s 更改为MULTISET_AGG
s 并且只在查询的顶层执行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 的人,尽管我将那里的示例简化为单层嵌套而犯了错误。我提出这个只是为了指出使用横向连接的解决方法,但是,正如你所说,这种方法最终需要根据了解实现来提取值。以上是关于如何按嵌套多重集中的值排序?的主要内容,如果未能解决你的问题,请参考以下文章