如何在保持小结果集的同时从SQL填充对象的列表属性?

问题描述

对于Java应用程序中的(PDF-)报告,我从PostgreSQL 9.6数据库中查询各种数据.传递给报告的数据还包含列表,而列表又包含其他列表.但总体而言,报告不包含那么多值(值的数量在几百个之内).

为了查询数据,我使用一个带有多个连接(大约8个)的巨大SQL语句来查询列表中的数据和列表中的列表.但是,连接自然会导致在输出中复制很多行.例如.以下查询:

select *
from (values(1,'item A'), (2, 'item B')) items(id, label)
left join (values(1, 'subItem A1'), (1, 'subItem A2'), (1, 'subItem A3')) subitems(itemId, label) on items.id=subitems.itemId
left join (values(1, 'sub2Item A1'), (1, 'sub2Item A2'), (1, 'sub2Item A3')) subitems2(itemId, label) on items.id=subitems2.itemId

8个值只返回10行,这很容易使用.但是,再加上另一个子项目表,只有3个以上的值将包含28行(总共11个值):

select *
from (values(1,'item A'), (2, 'item B')) items(id, label)
left join (values(1, 'subItem A1'), (1, 'subItem A2'), (1, 'subItem A3')) subitems(itemId, label) on items.id=subitems.itemId
left join (values(1, 'sub2Item A1'), (1, 'sub2Item A2'), (1, 'sub2Item A3')) subitems2(itemId, label) on items.id=subitems2.itemId
left join (values(1, 'sub3Item A1'), (1, 'sub3Item A2'), (1, 'sub3Item A3')) subitems3(itemId, label) on items.id=subitems3.itemId

并且每次连接操作的行数都会快速增长.在我的帐户示例中,我在此期间编写的查询生成了大约五十万行,仅适用于平均大小的报告,只有几百个值.从查询构建列表(我使用MyBatis)工作正常,它很慢,消耗大量带宽并消耗大量内存,这实际上最终成为一个问题.

解决方案的想法

>一种选择是使用MyBatis的“嵌套选择”功能,它允许我使用N 1个查询自动检索列表.但是,某些连接会加入视图,这些视图需要对大型表进行分组和求和,如果只查询一次视图,那么使用这些视图中的数据填充两个列表要快得多.
>另一种可能性是通过分别从不同的表/视图中选择数据然后从这些数据填充对象的列表来执行Java中的一些连接操作.虽然这可以工作,但它忽略了SQL的强大功能,我需要自己复制SQL的功能.
>第三种可能性是简单地忽略SQL的表结构并构建分层结构,例如使用JSON:

select
  json_agg(jsonb_build_object(
        'id', items.id,
        'subA', subitems.list,
        'subB', subitems2.list,
        'subC', subitems3.list
)) result
from (values(1,'item A'), (2, 'item B')) items(id, label)
left join (
  select itemId, json_agg(label) list
  from
  (values(1, 'subItem A1'), (1, 'subItem A2'), (1, 'subItem A3')) subitems(itemId, label)
  group by itemId
) subitems on items.id = subitems.itemId
left join (
  select itemId, json_agg(label) list
  from
  (values(1, 'sub2Item A1'), (1, 'sub2Item A2'), (1, 'sub2Item A3')) subitems(itemId, label)
  group by itemId
) subitems2 on items.id = subitems2.itemId
left join (
  select itemId, json_agg(label) list
  from
  (values(1, 'sub3Item A1'), (1, 'sub3Item A2'), (1, 'sub3Item A3')) subitems(itemId, label)
  group by itemId
) subitems3 on items.id = subitems3.itemId

后面的选项返回:

[{
  "id": 1,
  "subA": ["subItem A1", "subItem A2", "subItem A3"],
  "subB": ["sub2Item A1", "sub2Item A2", "sub2Item A3"],
  "subC": ["sub3Item A1", "sub3Item A2", "sub3Item A3"]
}, {
  "id": 2,
  "subA": null,
  "subB": null,
  "subC": null
}]

JSON结果是我可以在Java中轻松解析的结果.这个选项对我来说效率最高,因为它完全消除了数据重复,并且可以直接反序列化为已经具有正确结构的Java对象.然而,由于需要进行所有json_agg和jsonb_build_object调用,因此会产生轻微的可读性损失.

我想我不是第一个碰到这个问题的人.还有其他选择吗?是否有一些“普遍接受的最佳做法”来处理这个问题?我对可能的选项的分析是否正确?

最佳答案 你不能用DBMS做一些操作吗?

如果为平均大小的报告生成超过50万行,我猜您正在检索一些数据并在Java代码中对其进行计算.

您可以创建一些视图或聚合表来简化查询.

点赞