基于行之间共享属性的新ID

时间:2020-04-09 22:18:33

标签: sql amazon-web-services amazon-redshift

我有一个表transactions,其中有列account_iddevice_idcard_id。我想创建一个新列token定义为用户ID 。在共享至少前面提到的三列之一的值的交易之间,此新列将具有相同的值。

这个想法是创建一个用户标识符,该用户标识符由帐户,设备和卡组成。这个新ID比以前使用任何ID都“强”,例如,如果某人更改了他的帐户或设备,但没有更改他的卡,则他的交易仍将链接此新令牌ID之前的令牌。

在Redshift SQL中,哪种方法可以做到这一点?

示例:

transaction_id account_id device_id card_id token
1              1          1         1       1
2              1          2         2       1
3              2          2         3       1
4              3          3         3       1
5              4          4         4       2
6              5          4         5       2
7              6          8         2       1
8              7          7         7       3

在上面的示例中:

    自第一次交易以来,
  • T1的令牌为1。
  • T2具有令牌1,因为已通过帐户将其链接到T1。
  • T3由于已通过设备链接到T2,因此具有令牌1。
  • T4由于已通过卡链接到T3,因此具有令牌1。
  • T5具有令牌2,因为这3个字段中的任何一个都与先前的交易(新用户)共享一个值。
  • T6由于已通过设备链接到T5,因此具有令牌2。
  • T7由于已通过卡链接到T2,因此具有令牌1。
  • T8具有令牌3,因为这3个字段中的任何一个都与先前的交易(新用户)共享一个值

更新

我设法通过两个步骤获得了解决方案:

  1. 对于每笔交易,我都会使用与3个ID(帐户,设备或卡)中的任何一个关联的最早交易的token更新transaction_id列。此步骤不能解决问题,因为例如T4应该具有令牌1,但是它具有令牌2,因为链接到T4的最早的事务是T2,而不是T1。
  2. 一旦每个事务都链接了最早的事务,我将更新所有事务(其令牌不是相同的事务ID,只是出于性能方面的考虑(因为不需要)),然后更改其令牌作为链接的事务的令牌。通过一次在不同的SQL事务中一次一行地递增执行此操作,可以得到预期的输出。

这是代码

create table sandbox.test(
transaction_id integer,
account_id integer,
device_id integer,
card_id integer
);

insert into sandbox.test values
(1, 1, 1, 1),
(2, 1, 2, 2),
(3, 2, 2, 3),
(4, 3, 3, 3),
(5, 4, 4, 4),
(6, 5, 4, 5),
(7, 6, 8, 2),
(8, 7, 7, 7),
(9, 3, 9, 9),
(10, 10, 9, 9);

alter table sandbox.test
add column token int
default NULL;

-- Step 1
update sandbox.test
set token = A.token
from (
    with links as (
    select  transaction_id, 
            first_value(transaction_id) 
               over(partition by account_id order by transaction_id rows unbounded preceding) as account_link,
            first_value(transaction_id) 
               over(partition by device_id order by transaction_id rows unbounded preceding) device_link,
            first_value(transaction_id) 
               over(partition by card_id order by transaction_id rows unbounded preceding) card_link
    from sandbox.test
    )
    select l1.transaction_id, least(l2.account_link, l2.device_link, l2.card_link) token
    from links l2 
    inner join links l1 
       on l2.transaction_id=least(l1.account_link, l1.device_link, l1.card_link)
) A
where sandbox.test.transaction_id=A.transaction_id;

-- Step 2
CREATE OR REPLACE PROCEDURE refresh_transactions() AS $$
DECLARE
  transactions RECORD;
BEGIN
  FOR transactions IN SELECT transaction_id FROM sandbox.test ORDER BY transaction_id LOOP
    RAISE INFO '%', transactions.transaction_id;
    EXECUTE 'update
            sandbox.test
            set token = (select A.token from sandbox.test A where
                                        A.transaction_id = sandbox.test.token)
            where transaction_id='||transactions.transaction_id||';';
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql;

CALL refresh_transactions();

但是,此解决方案的第二部分(refresh_transactions()调用)花费的时间太长,无法在具有数百万个事务的DB上运行,因此无法实现。也许有一种方法可以更有效地做到这一点?

0 个答案:

没有答案