在左表中没有开销的情况下获取大树

时间:2017-08-20 09:05:26

标签: sql postgresql join tree

我的问题更具理论性,它是关于为什么RDBMS /驱动程序以他们所有人的方式返回数据,而不是他们如何找到正确的集合,以及如何找到它。我对SQL非常熟悉,但有一件事总是让我的经济感觉烦恼。

考虑遵循“类”图:

A {
    field1, ..., field9
    b_items = [ b1, ..., bN ]
}

B {
    field1, ..., field6
    c_items = [ c1, ..., cM ]
}    

C {
    field1, field2
}

我们有很少的A对象,每个对象都有很多B对象,每个B对象都有很多C对象。 count(A) < count(B) << count(C)

现在我想使用RDBMS来存储它,因为关系很酷并且优化器很聪明,所以如果有一个好的计划和索引集,我几乎可以在几毫秒内得到任何东西。

我将跳过表格创建代码,这应该是显而易见的,然后直接进入选择:

SELECT *
FROM A
LEFT JOIN B ON B.a_id = A.id
LEFT JOIN C ON C.b_id = B.id
WHERE whatever

数据库服务器返回所有表中所有列组合的结果集,并正确地连接到排序树中:

A.f1 .... A.f9  B.f1 .... B.f6  C.f1 C.f2
---------------------------------------------------
   1    1    1     1    1    1     1    1
   1    1    1     1    1    1     2    2
   1    1    1     1    1    1     3    3
   ... more rows...
   1    1    1     1    1    1   999  999
                        ↓
   1    1    1     2    2    2     1    1
   1    1    1     2    2    2     2    2
   ... more rows...
   1    1    1     2    2    2   999  999
   ... lots of rows ...
   1    1    1    99   99   99   999  999
        ↓
   2    2    2 -- oh there it is, A[2]
   ...
   5    5    5  NULL NULL NULL  NULL NULL -- A[5] has no b_items
   ...
   9    9    9 ...

问题是,如果A有很多列,特别是有文本,json,其他重要数据,它会被重复数千次以匹配+ B + C连接的每个产品。为什么SQL服务器至少只是在加入组中的第一个之后向我发送相同的 {A,B} -rows?理想情况下,我希望看到类似的结果:

[
  {
    <A-fields>,
    B = [
      {
        <B-fields>,
        C = [
          {
            <C-fields>
          },
          ... more C rows
        ]
      },
      ... more B rows
    ]
  },
  ... more A rows
]

这非常类似于我实际需要在客户端获取内存。我知道我可以提出更多查询以获取更少的数据,例如通过A.id IN (ids...)或存储过程在寄生虫行上返回空值,但不是用于一次性访问的关系模型?往返很重,计划者猜测也是如此。实际数据图很少只有3个步高(考虑5-10)。那么为什么不通过单程通过,但没有过多的流量?

我很喜欢A和B列中的重复单元格,因为通常没有太多,但也许我缺少一些主流, SQL 非hacky 谷歌隐藏了这么多年。

谢谢!

2 个答案:

答案 0 :(得分:1)

避免重复数据传输的唯一方法是使用string_agg ()array_agg ()等聚合函数。您还可以使用jsonb函数聚合数据。您甚至可以获取单个json对象而不是表格数据,例如:

select jsonb_agg(taba)
from (
    select to_jsonb(taba) || jsonb_build_object('tabb', jsonb_agg(tabb)) taba
    from taba
    left join (
        select to_jsonb(tabb) || jsonb_build_object('tabc', jsonb_agg(to_jsonb(tabc))) tabb
        from tabb
        join tabc on tabc.bid = tabb.id
        group by tabb.id
        ) tabb 
    on (tabb->>'aid')::int = taba.id
    group by taba.id
) taba

Complete working example.

答案 1 :(得分:1)

json_agg()可能不是最快的东西。而且,我想知道你的ORM是否会正确地消化它并实例化正确的对象。

通常的做法是:

SetInt(..)

然后你恢复了ids,然后执行:

SELECT ... FROM a WHERE ...

这些是由ORM完全自动生成的。如果ORM是智能的,则每个表可以获得一个查询。如果它是愚蠢的,你会得到每个对象一个查询...但是,这会强制三个查询,网络回合,加上一些处理。幸运的是,postgres会让你吃蛋糕并吃掉它,虽然这需要一些额外的工作。

因此,您可以在plpgsql中创建一个返回“SETOF refcursor”的函数。由于refcursor是游标,因此函数可以返回多个结果集。

Example

回到我为网站做sql的那天,我用了几次。大多数情况下,当您只想获取一个对象和一些依赖项时,实际的查询解析和计划所花费的时间比返回一行或几行的查询本身要长。在那里它使用一个函数,所以一切都已编译。这非常有效。