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