Новый идентификатор на основе общего атрибута между строками - PullRequest
0 голосов
/ 10 апреля 2020

У меня есть таблица transactions со столбцами account_id, device_id и card_id. Я хотел бы создать новый столбец token, определенный как идентификатор пользователя . Этот новый столбец будет иметь одинаковое значение между транзакциями, которые разделяют значения по крайней мере одного из 3 упомянутых ранее столбцов.

Идея состоит в том, чтобы создать идентификатор пользователя , который представляет собой сочетание учетной записи, устройство и карта. Этот новый идентификатор «сильнее», чем любой из предыдущих, в том смысле, что, например, если кто-то меняет свою учетную запись или устройство, но не свою карту, его транзакции по-прежнему будут связаны с предыдущей по этому новому идентификатору токена.

Какой простой способ сделать это в 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 имеет токен 1, поскольку он связан с T2 устройством.
  • T4 имеет токен 1, поскольку он связан с T3 картой .
  • T5 имеет токен 2, поскольку любое из трех полей имеет общее значение с предыдущими транзакциями (новый пользователь).
  • T6 имеет токен 2, поскольку связано с T5 устройством.
  • T7 имеет токен 1, так как связан с T2 картой.
  • T8 имеет токен 3, поскольку любое из 3 полей имеет значение с предыдущими транзакциями (новый пользователь)

ОБНОВЛЕНИЕ

Мне удалось получить решение, основанное на 2 шагах:

  1. Для каждой транзакции я обновляю столбец token на transaction_id самой старой транзакции связан с любым из 3 идентификаторов (учетная запись, устройство или карта). Этот шаг не решает проблему, потому что, например, T4 должен иметь токен 1, но он имеет токен 2, поскольку самая старая транзакция, связанная с T4, - это T2, а не T1.
  2. Как только каждая транзакция имеет самую старую транзакцию, связанную Я обновляю все транзакции (чей токен не совпадает с идентификатором транзакции, только для производительности, поскольку не требуется), и я изменяю его токен на токен связанной транзакции. Делая это постепенно по одной строке за раз и в различных 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()) занимает слишком много времени для запуска на моей БД с миллионами транзакций, поэтому его невозможно реализовать. Может быть, есть способ сделать это более эффективно?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...