【问题标题】:Aggregate column text where dates in table a are between dates in table b聚合列文本,其中表 a 中的日期介于表 b 中的日期之间
【发布时间】:2017-01-13 01:36:57
【问题描述】:

样本数据

CREATE TEMP TABLE a AS
SELECT id, adate::date, name
FROM ( VALUES 
  (1,'1/1/1900','test'),
  (1,'3/1/1900','testing'),
  (1,'4/1/1900','testinganother'),
  (1,'6/1/1900','superbtest'),
  (2,'1/1/1900','thebesttest'),
  (2,'3/1/1900','suchtest'),
  (2,'4/1/1900','test2'),
  (2,'6/1/1900','test3'),
  (2,'7/1/1900','test4')
) AS t(id,adate,name);

CREATE TEMP TABLE b AS
SELECT id, bdate::date, score
FROM ( VALUES
  (1,'12/31/1899', 7 ),
  (1,'4/1/1900'  , 45), 
  (2,'12/31/1899', 19), 
  (2,'5/1/1900'  , 29), 
  (2,'8/1/1900'  , 14)
) AS t(id,bdate,score);

我想要什么

我需要做的是聚合表 a 中的列文本,其中 id 与表 b 匹配,表 a 中的日期介于表 b 中最接近的两个日期之间。期望的输出:

id  date    score   textagg
1   12/31/1899  7   test, testing
1   4/1/1900    45  testinganother, superbtest
2   12/31/1899  19  thebesttest, suchtest, test2
2   5/1/1900    29  test3, test4
2   8/1/1900    14  

我的想法是做这样的事情:

create table date_join
select a.id, string_agg(a.text, ','), b.*
from tablea a
left join tableb b
on a.id = b.id
*having a.date between b.date and b.date*;

但我真的在最后一行苦苦挣扎,弄清楚如何仅聚合表 b 中的日期在表 b 中最接近的两个日期之间的位置。非常感谢任何指导。

【问题讨论】:

  • id 中具有重复值的架构没有多大意义。话虽如此,请查看tsrange 数据类型,看看是否可以将其用于此目的
  • 您的示例已损坏,superbtest 不是表b 中的介于 匹配日期之间。也缺少表定义和 Postgres 版本。
  • 请注意,您想要的结果不是第一范式。有 9 行输出的 (id, date, score, text) 之类的东西不是更好吗?
  • @ErwinBrandstetter 你是对的,我忘记指定的一件事是,对于最后一个日期,我想要它之前的所有文本。
  • @FabianPijcke 通常是的,但在这种情况下,我将导出表格用于不同的目的。

标签: sql postgresql date left-join aggregates


【解决方案1】:

我不能保证这是最好的方法,但这是一种方法。

with b_values as (
  select
    id, date as from_date, score,
    lead (date, 1, '3000-01-01')
      over (partition by id order by date) - 1 as thru_date
  from b
)
select
  bv.id, bv.from_date, bv.score,
  string_agg (a.text, ',')
from
  b_values as bv
  left join a on
    a.id = bv.id and
    a.date between bv.from_date and bv.thru_date
group by
  bv.id, bv.from_date, bv.score
order by
  bv.id, bv.from_date

我假设您的表格中的日期永远不会超过 2999 年 12 月 31 日,因此如果您在该日期之后仍在运行此查询,请接受我的歉意。

这是我运行此程序时得到的输出:

id  from_date   score   string_agg
1   0           7       test,testing
1   92          45      testinganother,superbtest
2   0           19      thebesttest,suchtest,test2
2   122         29      test3,test4
2   214         14  

我可能还注意到,连接中的between 是性能杀手。如果您有大量数据,可能会有更好的方法来解决这个问题,但这在很大程度上取决于您的实际数据是什么样的。

【讨论】:

  • 您可以通过使用日期为 1800-01-01 的 lag() 来减少道歉的机会。当然,你不能幸免于时间旅行的发明,但是你的查询不应该存在,除非它被用来运行时间旅行机器(你有麻烦了!)
  • 我已将示例数据添加到问题中,这些数据可能适用于您的数据,也可能不适用于您的数据。不过,如果您使用 DDL 更新问题本身,那将来会很棒,您曾经使环境正常运行。
  • 这通常很好,如果你想预先计算范围并找到日期在里面,你可以使用tsrange。这是一个轻微的语法。但是,不可能更快。您也可以将 'infinity'::date 放在顶部派对中,只是为了摆脱怪异的 3000-01-01 哈哈。
  • 我完全忘记了无穷大……这是个不错的选择。
  • @Hambone 我会在今晚晚些时候再次访问我的机器时尝试这个。作为参考,表 a 有约 318k 行,表 b 有 66k 行。这是一次性导出,因此根据性能我可能不必寻找between 的替代品。
猜你喜欢
  • 2021-12-30
  • 1970-01-01
  • 2020-11-06
  • 2021-06-04
  • 2021-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多