用于按字段值配对行的SQL

问题描述 投票:0回答:2

我有一张桌子:

create table transaction_log (
  id serial, 
  operation_type character varying(36),
  date timestamp with time zone,
  sum   double precision,
  user_id integer,
  PRIMARY KEY(id)
);

它保留了两种类型的操作:blockunblock用于多个用户(contragent_id)和lot(lot_id)和operation的datetime字段。

  • 一个用户可以具有多个阻止和未阻止的操作
  • 具有块操作的记录之后可以具有一个解除阻塞操作。
  • 阻止和解除阻塞是顺序的。必须有一个用户块,下一个块只能在解除阻塞操作后发生。
  • 取消阻止日期和时间可以与块相同。它意味着被阻止并立即解锁。
  • 一个用户可以在混合时间轴上具有多个块和解块操作序列。
  • id是唯一的

例如:

id, sum, operation_type, date, user_id
1, 5900, blocked, 2018-01-05 11:00, 1
2, 3500, blocked, 2018-01-08 12:00, 2
3, 5900, unblock, 2018-02-11 09:00, 1
4, 1000, blocked, 2018-01-09 05:00, 3
5, 3500, unblock, 2018-01-24 19:00, 2 

所以我需要让SQL获取具有相应解除阻塞操作日期的所有块操作(如果存在)。例如:block_ID,sum,blocked_date,unblock_date。所以,从我需要获得的示例数据:例如:

block_ID, sum, blocked_date, unblock_date
1, 5900, 2018-01-05 11:00, 2018-02-11 09:00
2, 3500, 2018-01-08 12:00, 2018-01-24 19:00
4, 1000, blocked, 2018-01-09 05:00, null

我想我需要一个WITH语句,但我无法理解如何正确匹配记录。

任何帮助赞赏。

BTW Postgres 9.4

sql postgresql postgresql-9.4
2个回答
0
投票

你可以尝试以下方式

with block as
     (
    select * from transactions
    where operation='blocked'
    ),
    unblock as 
    (
    select * from transactions
    where operation='unblock'
    )
    select block.id as block_ID, block.sum,
    block.date, unblock.date from block
    left join unblock on block.user_id=unblock.user_id

0
投票

如果您的数据一致,那么您只是在寻找阻止日期之后的最小取消阻止日期。您可以在SELECT子句中的子查询中获取此内容:

select user_id, sum, date as block,
(
  select min(ub.date)
  from blocktable ub
  where ub.operation_type = 'unblock'
  and ub.user_id = b.user_id
  and ub.date >= b.date
) as unblock
from blocktable b
where operation_type = 'blocked';

或者在带有横向连接的FROM子句中:

select b.user_id, b.sum, b.date as block, ub.unblock
from blocktable b
left join lateral
(
  select min(ub.date) as unblock
  from blocktable ub
  where ub.operation_type = 'unblock'
  and ub.user_id = b.user_id
  and ub.date >= b.date
) b
where operation_type = 'blocked';

使用横向连接甚至可以获得整行:

select *
from blocktable b
left join lateral
(
  select *
  from blocktable ub
  where ub.operation_type = 'unblock'
  and ub.user_id = b.user_id
  and ub.date >= b.date
  order by ub.date
  fetch first 1 row only
) as unblock
where operation_type = 'blocked';

获得单一日期的另一个选择是LEAD

select user_id, sum, block, unblock
from
(
  select
    user_id,
    sum,
    date as block,
    lead(date) over (partition by user_id order by date, operation_type) as unblock,
    operation_type
  from mytable
) block_and_unblock
where operation_type = 'blocked';

如果您希望查询在不一致的情况下工作得很好(特别是两个阻塞或两个未阻塞的记录彼此跟随),您可能必须按顺序选择数据,即在递归查询中或使用应用程序。

© www.soinside.com 2019 - 2024. All rights reserved.