假设我们在1:N关系中有两个表。现在,假设在java端,您希望将结果数据构建为这样的结构:
class ParentDto {
public Long id;
public String someColumn;
public List<ChildDto> children = new ArrayList<>();
}
class ChildDto {
public Long id;
public Long parentId;
public String someColumn;
}
从理论上讲,执行单个sql查询是否更快,如:
SELECT *
FROM PARENT_DTO
JOIN CHILD_DTO ON PARENT_DTO.ID = CHILD_DTO.PARENT_ID
然后在java端执行像这样的分组操作。
// assume that query is some api to run a query like jdbc that returns theortical type List<Row>
List<ParentDto> results = query(/* query above*/).stream()
// assume that first value in row is ParentDto::id
.collect(groupingBy(row -> (Long) row.get(0)))
// after collect stream type is Map<Long, List<Row>>, whatever row type is from whatever api
.values()
.stream()
// listOfGroupRows is just List<Row>
.map(listOfGroupedRows -> {
// assume that ParentDto constructor knows how to fetch values from first row
ParentDto rowValue = new ParentDto(listOfGroupedRows.get(0))
// assume that ChildDto constructor knows how to fetch values from each row
rowValue.children = listOfGroupedRows.stream()
.map(row -> new ChildDto(row))
.collect(toList())
})
或只运行两个单独的查询更快:
SELECT *
FROM PARENT_DTO
SELECT *
FROM CHILD_DTO
然后在java端执行类似这样的分组操作:
Map<Long, List<ChildDto>> children = query(/* second query */).stream()
.map(r -> new ChildDto(r))
.collect(groupingBy(ChildDto::parentId));
List<ParentDto> results = query(/* first query */).stream()
.map(r -> {
ParentDto dto = new ParentDto(r);
dto.children = children.get(dto.id);
return dto;
})
.collect(toList());
第一种方法只需要对数据库进行一次查询,但卷较高,因为返回的行数为n*m,其中n为父表中的行数,m为子表中的行数。它还会在java端产生2(n * m)的开销(一对组,一组转换每一行)。
第二个方法对数据库产生两个查询,但卷较低,因为返回的行总数为n+m,每一行比原始联接查询的每一行都小。java性能更好,因为它也是n+m (m用于分组子代,n用于处理和初始化父级)。
按照这种逻辑,我总是选择选项二,但我对实际的sql服务器没有太多的实际经验。是否有更有经验的个人会/应该选择备选方案1的情况?另一次向数据库提交的成本会超过这些操作的复杂性差异吗?
发布于 2018-08-09 07:00:00
我并不是说这是一个问答风格的帖子,但睡觉后,我意识到我做了一个错误的假设。不同类型的联接具有不同的复杂性。我提到的复杂性(n*m)是交叉连接的内存和计算复杂度,其中两个集合的结果是原始集合的幂集。我最初的联接查询不是交叉连接,而是内部连接。在最坏的情况下,内存复杂度和计算复杂度都是最大值(n,m),但取决于两个表中有多少行不以任何方式与另一个表关联(没有子表的parent_dto不在结果集中)。这也只适用于简单的分组解决方案,因为sql servers使用了许多方法来从根本上改进连接,比如索引操作。
因此,总之,在内部和左联接的实践中,方法1的复杂度较低(max(m,n) vs. m+ n),而且由于索引和其他预calc和缓存方法的存在,它在sql端的解析速度要快得多。唯一的区别是网络开销(冗余规范化数据)略有增加。
发布于 2018-08-08 22:01:59
如果您使用的是像Hibernate这样的ORM框架,这将自动在幕后得到处理。而且,它将使用第一个版本,其中连接发生在数据库上,而不是第二个版本。第一个版本更可取的原因是数据库被设计成非常高效地执行连接之类的任务。Java在执行相同的连接操作时可能效率较低。我甚至看到第二个版本可能更高效的唯一原因是数据往返于数据库的延迟。第二个版本可能涉及传输较少的数据。但是,我认为在Java代码中执行数据库操作的代价将大于此。
https://stackoverflow.com/questions/51759711
复制