Выполнение JOIN с наиболее подходящим временем - PullRequest
0 голосов
/ 12 июля 2020

В качестве упрощенного сценария предположим, что у меня есть следующие таблицы:

deal_views:

user_id | viewed_at           | more attributes ...
------------------------------
1       | 2020-07-12 15:00:00 | ...
1       | 2020-07-12 14:00:00 | ...
1       | 2020-07-12 13:00:00 | ...
1       | 2020-07-03 09:00:00 | ...
2       | 2020-07-12 15:00:00 | ...
2       | 2020-07-12 14:00:00 | ...
2       | 2020-07-12 13:00:00 | ...
3       | 2020-07-12 15:00:00 | ...

покупок:

user_id | purchased_at        | more attributes ...
------------------------------
1       | 2020-07-03 09:02:10 | ...
1       | 2020-07-12 14:04:53 | ...
2       | 2020-07-12 15:20:12 | ...

(Не каждый пользователь, просматривающий сделки совершат покупку; некоторые пользователи будут просматривать предложения несколько раз, прежде чем совершить покупку; некоторые пользователи сделают несколько покупок. Каждый пользователь, совершивший покупку, должен просмотреть предложения хотя бы один раз; возможно, непосредственно перед покупкой!)

Я хочу выбрать deal_views (включая некоторые дополнительные атрибуты; для простоты опущены выше), которые произошли совсем недавно перед покупкой для каждого пользователя. Мотивация здесь заключается в том, что видимые сделки меняются с течением времени, поэтому мы хотели бы знать, «в каком состоянии были сделки в последний раз перед покупкой?».

Итак, для приведенные выше данные будут выглядеть так:

user_id | viewed_at           | more attributes ...
------------------------------
1       | 2020-07-03 09:00:00 | ...
1       | 2020-07-11 14:00:00 | ...
2       | 2020-07-11 15:00:00 | ...

SQL производительность была бы хорошей, но это не большая проблема (пока она работоспособна!), поскольку это всего лишь одноразовый запрос. sh выполнить. В таблице результатов должно быть примерно 200 000 строк.

Ответы [ 2 ]

2 голосов
/ 12 июля 2020

Snowflake поддерживает боковые соединения, поэтому вы можете:

select p.*, d.*
from purchases p left join lateral
     (select d.*
      from deals d
      where d.user_id = p.user_id and
            d.viewed_at < p.purchased_at
      order by d.viewed_at desc
      limit 1
     ) d
     on 1=1;

Вы также можете row_number():

select pd.*
from (select p.*, d.*,   -- select the columns explicitly to avoid duplicate column names
            row_number() over (partition by p.user_id, p.purchased_at order by d.viewed_at desc) as seqnum
      from purchases p left join
           deals d
           on d.user_id = p.user_id and d.viewed_at < p.purchased_at 
     ) pd
where seqnum = 1;
0 голосов
/ 12 июля 2020

( Заявление об ограничении ответственности: У меня нет Snowflake, поэтому я не пробовал и не тестировал. Я тестировал его на MS SQL.)

/*
DECLARE @deal_views AS TABLE (
    id int PRIMARY KEY IDENTITY(1,1), 
    [user_id] INT NOT NULL,
    viewed_at DATETIME NOT NULL);

DECLARE @purchases AS TABLE (
    id int PRIMARY KEY IDENTITY(1,1), 
    [user_id] INT NOT NULL,
    purchased_at DATETIME NOT NULL);

INSERT INTO @deal_views VALUES
    (1, '2020-07-12 15:00:00'), (1, '2020-07-12 14:00:00'), (1, '2020-07-12 13:00:00'),
    (1, '2020-07-03 09:00:00'),
    (2, '2020-07-12 15:00:00'), (2, '2020-07-12 14:00:00'), (2, '2020-07-12 13:00:00'),
    (3, '2020-07-12 15:00:00')

INSERT INTO @purchases VALUES
    (1, '2020-07-03 09:02:10'),
    (1, '2020-07-12 14:04:53'),
    (2, '2020-07-12 15:20:12')*/

SELECT p.[user_id], MAX(d.viewed_at) AS viewed_at
FROM purchases p
JOIN deal_views d
    ON  p.[user_id] = d.[user_id]
    AND p.purchased_at > d.viewed_at
GROUP BY p.[user_id], p.purchased_at

Вывод :

Output

DB Fiddle Demo

Если вам нужны другие атрибуты из deal_views, присоедините их обратно к представлению сделок.

SELECT d.id,
       d.user_id,
       d.viewed_at,
       d.attributes2, d.attributes3, d.attributes4
FROM (
    SELECT p.[user_id], MAX(d.viewed_at) AS viewed_at
    FROM purchases p
    JOIN deal_views d
        ON  p.[user_id] = d.[user_id]
        AND p.purchased_at > d.viewed_at
    GROUP BY p.[user_id], p.purchased_at) mv
JOIN deal_views d
    ON  mv.[user_id] = d.[user_id]
    AND mv.viewed_at > d.viewed_at
...