PostgreSQL:自引用,展平连接到包含对象树的表

时间:2012-09-26 21:08:26

标签: sql postgresql join tree self-reference

我有一个相对较大的(如在> 10 ^ 6条目中)称为“东西”的表,它表示可定位的对象,例如,国家,地区,城市,街道等。它们被用作具有固定深度的对象树,因此表格结构如下所示:

id
name
type
continent_id
country_id
city_id
area_id
street_id
etc.

“事物”内的关联是1:n,即街道或地区总是属于定义的城市和国家(不是两个或没有);例如,city_id列包含该城市内所有对象的“城市”事物的id。 “类型”列包含事物类型(街道,城市等)作为字符串。

此表在另一个表“actions”中被引用为“thing_id”。我正在尝试生成一个操作位置统计信息表,显示给定位置的活动和非活动操作数。一个简单的JOIN,如

SELECT count(nullif(actions.active, 1)) AS icount, 
       count(nullif(actions.active, 0)) AS acount, 
       things.name AS name, things.id AS thing_id, things.city_id AS city_id
  FROM "actions" 
  LEFT JOIN things ON actions.thing_id = things.id 
 WHERE UPPER(substring(things.name, 1, 1)) = UPPER('A') 
   AND actions.datetime_at BETWEEN '2012-09-26 19:52:14' AND '2012-10-26 22:00:00'
 GROUP BY things.name, things.id ORDER BY things.name

将为我提供一个“事物”列表(以“A”开头),其中包含与其相关的操作及其活动和非活动计数,如下所示:

icount | acount | name                      | thing_id | city_id
------------------------------------------------------------------
     0        5   Brooklyn, New York City   | 25       | 23
     1        0   Manhattan, New York City  | 24       | 23
     3        2   New York City             | 23       | 23

现在我想

  • 只考虑“城市”事物(这很简单:按“事物”类型过滤)和
  • 在有效/无效计数中,使用此城市中发生的所有操作的总和 - 无论该操作是与城市本身相关还是城市内部(=具有相同的city_id) )。使用与上面相同的数据集,新查询应该导致
icount | acount | name                      | thing_id | city_id
------------------------------------------------------------------
     4        7   New York City             | 23       | 23

我不需要这个表中的thing_id(因为它不会是唯一的),但是因为我确实需要城市的名字(用于显示),所以也可能同样容易输出ID,然后我不必在我的代码中进行更改。

我如何修改上述查询才能实现此目的?如果可能的话,我想避免额外访问数据库,以及高级SQL功能,如过程,触发器,视图和临时表。

我在Rails 3.0.14上使用Postgres 8.3和Ruby 1.9.3(在Mac OS X 10.7.4上)。

谢谢! :)

1 个答案:

答案 0 :(得分:2)

您需要在独立子查询中计算城市中所有事物的操作,然后加入一组有限的事物:

SELECT c.icount
      ,c.acount      
      ,t.name
      ,t.id AS thing_id
      ,t.city_id
FROM  (
   SELECT t.city_id
         ,count(nullif(a.active, 1)) AS icount
         ,sum(a.active) AS acount
   FROM   things t 
   LEFT   JOIN actions a ON a.thing_id = t.id 
   WHERE  t.city_id = 23           -- to restrict results to one city
   GROUP  BY t.city_id
   ) c                             -- counts per city
JOIN   things t USING (city_id)
WHERE  t.name ILIKE 'A%'
AND    t.datetime_at BETWEEN '2012-09-26 19:52:14'
                         AND '2012-10-26 22:00:00'
ORDER  BY t.name, t.id;

我还简化了查询中的许多其他内容,并使用了表别名来使其更易于阅读。