【问题标题】:SQL query : multiple challengesSQL 查询:多重挑战
【发布时间】:2021-03-02 06:18:33
【问题描述】:

不是 SQL 专家,我正在努力解决以下问题:

我继承了一个大型表(大约 1 亿行),其中包含时间戳事件,这些事件代表大多数短暂现象的阶段转换。不幸的是,事件的记录方式有点奇怪,表格如下:

phen_ID   record_time  producer_id   consumer_id  state   ...

000123    10198789                               start
          10298776     000123        000112      hjhkk
000124    10477886                               start
          10577876     000124        000123      iuiii
000124    10876555                               end

每个现象(phen-ID)都有一个开始事件和一个理论上的结束事件,尽管它可能还没有发生,因此没有记录下来。然后,每种现象都可以经历几种状态。不幸的是,对于某些州,ID 记录在产品或消费者字段中。另外,状态的数量不是固定的,状态之间的时间也不是固定的。

首先,我需要创建一个 SQL 语句,为每个 phen-ID 显示开始时间和最后记录事件的时间(可以是结束状态或中间状态之一)。

仅考虑单个 phen-ID,我设法将以下 SQL 组合在一起:

WITH myconstants (var1) as (
   values ('000123')
)

select min(l.record_time), max(l.record_time) from 
   (select distinct *  from public.phen_table JOIN myconstants ON var1 IN (phen_id, producer_id, consumer_id)
 ) as l

由于特定现象的起始状态始终具有最低记录时间,因此上述语句正确地将记录的时间范围返回为一行,而与结束状态无关。

显然这里我必须手动提供 phen-ID。

如何进行这项工作,以便为每个唯一的 phen-ID 获得一行开始时间和最长记录时间?尝试适应select distinct phen-id ... 之类的东西,但无法将它们自动“喂”到上面。还是我在这里完全不合时宜?

补充: 澄清一下,使用上表的理想输出应该是这样的:

ID         min-time      max-time
000123     10198789      10577876   (min-time is start, max-time is state iuii)
000124     10477886      10876555   (min-time is start, max-time is end state)

【问题讨论】:

  • 今日提示:始终使用现代、明确的JOIN 语法。更容易编写(没有错误),更容易阅读和维护,如果需要更容易转换为外连接。
  • 如何将“或”子句“翻译”成正确的(自)JOIN 形式?这对我来说不是很清楚。
  • 从 public.phen_table JOIN myconstants ON var1 IN (phen_id, producer_id, consumer_id)
  • 请用您正在运行的数据库标记您的问题:mysql、oracle、postgresql...?
  • @jarih 添加了加入版本。谢谢!

标签: sql postgresql aggregate-functions lateral-join postgresql-13


【解决方案1】:

union all 可能是一个选项:

select phen_id, 
    min(record_time) as min_record_time, 
    max(record_time) as max_record_time
from (
    select phen_id, record_time from phen_table
    union all select producer_id, record_time from phen_table
    union all select consumer_id, record_time from phen_table
) t
where phen_id is not null
group by phen_id

另一方面,如果你想要优先级,那么你可以使用coalesce()

select coalesce(phen_id, producer_id, consumer_id) as phen_id, 
    min(record_time) as min_record_time, 
    max(record_time) as max_record_time
from phen_table
group by coalesce(phen_id, producer_id, consumer_id)

这两个查询的逻辑并不完全相同。如果存在三列中不止一个不是null,并且值不同的行,则第一个查询考虑所有非null 值,而第二个查询仅考虑“第一个”非@987654327 @值。


编辑

在您最终标记的 Postgres 中,union all 解决方案可以通过横向连接更有效地表达:

select x.phen_id, 
    min(p.record_time) as min_record_time, 
    max(p.record_time) as max_record_time
from phen_table p
cross join lateral (values (phen_id), (producer_id), (consumer_id)) as x(phen_id)
where x.phen_id is not null
group by x.phen_id

【讨论】:

    【解决方案2】:

    我认为你在正确的轨道上。试试这个,看看它是否是你要找的:

    select
        min(l.record_time)
        ,max(l.record_time)
        ,coalesce(phen_id, producer_id, consumer_id) as [Phen ID]
    from public.phen_table
    group by coalesce(phen_id, producer_id, consumer_id)
    

    【讨论】:

    • 恐怕这不起作用,因为 ID 并不总是在 phen_id 字段中。如上所述,它也可以在“producer_id”或“consumer_id”中,在这种情况下,phen_id 将为空。
    • 当 producer_id 和 consumer_id 字段都有数据时,如何选择使用哪一个?
    • 外部进程确定在新记录状态下观察到的现象是生产者还是消费者,并将 ID 放在相应的列中。同一现象不可能同时处于任何状态。不幸的是,我无法改变这一点。 DB中有数以百万计的此类现象,并且还在不断增长。
    • 好的,要明确的是,三个 id 字段总是每行只有一条记录?
    • 我修改了答案。你能跑一下看看是否符合你的需要吗?
    猜你喜欢
    • 1970-01-01
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    • 2017-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多