1

我有一张桌子,上面有某个人在某个时间戳上访问某个城市:

城市访问:

person_id         city                timestamp
-----------------------------------------------
        1        Paris      2017-01-01 00:00:00
        1    Amsterdam      2017-01-03 00:00:00
        1     Brussels      2017-01-04 00:00:00
        1       London      2017-01-06 00:00:00
        2       Berlin      2017-01-01 00:00:00
        2     Brussels      2017-01-02 00:00:00
        2       Berlin      2017-01-06 00:00:00
        2      Hamburg      2017-01-07 00:00:00

另一个表格列出了一个人购买冰淇淋的时间:

冰淇淋事件:

person_id      flavour                timestamp
-----------------------------------------------
        1      Vanilla      2017-01-02 00:12:00
        1    Chocolate      2017-01-05 00:18:00
        2   Strawberry      2017-01-03 00:09:00
        2      Caramel      2017-01-05 00:15:00

对于city_visits表中的每一行,我需要加入同一个人的下一个冰淇淋活动,以及它的时间戳和风味:

期望输出:

person_id       city            timestamp  ic_flavour          ic_timestamp
---------------------------------------------------------------------------
        1      Paris  2017-01-01 00:00:00     Vanilla   2017-01-02 00:12:00
        1  Amsterdam  2017-01-03 00:00:00   Chocolate   2017-01-05 00:18:00
        1   Brussels  2017-01-04 00:00:00   Chocolate   2017-01-05 00:18:00
        1     London  2017-01-06 00:00:00        null                  null
        2     Berlin  2017-01-01 00:00:00  Strawberry   2017-01-03 00:09:00
        2   Brussels  2017-01-02 00:00:00  Strawberry   2017-01-03 00:09:00
        2     Berlin  2017-01-06 00:00:00        null                  null
        2    Hamburg  2017-01-07 00:00:00        null                  null

我尝试了以下方法:

SELECT DISTINCT ON (cv.person_id, cv.timestamp)
  cv.person_id,
  cv.city,
  cv.timestamp,
  ic.flavour as ic_flavour,
  ic.timestamp as ic_timestamp
FROM city_visits cv
JOIN ice_cream_events ic
    ON ic.person_id = cv.person_id
   AND ic.timestamp > cv.timestamp

DISTINCT ON条款禁止在每次城市访问中加入除一个未来冰淇淋事件之外的所有事件。它可以工作,但是它不会自动选择第一个,而是似乎会为同一个人选择未来的任何冰淇淋事件。我可以添加的任何ORDER BY条款似乎都不会改变这一点。

解决该问题的理想方法是使子句在每次必须过滤掉重复项时都DISTINCT ON选择最小值。ic_timestamp

4

2 回答 2

2

由于没有cityin ice_cream_events,因此您的查询将在每次访问之前加入许多冰淇淋事件,然后再选择最早的事件。相反,我建议LEFT JOIN LATERAL,当有适当的索引支持时,这种情况会更快:

SELECT *
FROM   city_visits cv
LEFT   JOIN LATERAL (
   SELECT flavour AS ic_flavour, timestamp AS ic_timestamp
   FROM   ice_cream_events 
   WHERE  person_id = cv.person_id
   AND    timestamp > cv.timestamp
   ORDER  BY timestamp
   LIMIT  1
   ) ice ON true
ORDER  BY cv.person_id, cv.timestamp;

LEFT [OUTER] JOIN包括没有任何冰淇淋的访问。如果您只想吃冰淇淋,请切换到CROSS JOIN

在这种情况下,外部ORDER BY仅对结果行进行排序(与与 组合时不同DISTINCT ON,它还决定从每组对等点中选择哪一行)。

如果表很大,请确保有适当的索引以使其快速。理想情况下,复合索引按此ice_cream_events (person_id, timestamp, flavour)顺序排列在- 列上。对于外部排序。或者甚至可能允许另一个仅索引扫描。要看你的实际情况。这个例子显然是象征性的。city_visits (person_id, timestamp)city_visits (person_id, timestamp, city)

于 2017-09-15T12:07:31.987 回答
0

似乎该DISTINCT ON子句实际上遵循该ORDER BY子句。

结果,通过添加正确的顺序解决了问题:

SELECT DISTINCT ON (cv.person_id, cv.timestamp)
  cv.person_id,
  cv.city,
  cv.timestamp,
  ic.flavour as ic_flavour,
  ic.timestamp as ic_timestamp
FROM city_visits cv
JOIN ice_cream_events ic
    ON ic.person_id = cv.person_id
   AND ic.timestamp > cv.timestamp
ORDER BY cv.person_id, cv.timestamp ASC, ic.timestamp ASC  -- <- this line added
于 2017-09-15T10:58:07.157 回答