В тех случаях, когда обе таблицы имеют большое количество записей на посетителя, такое объединение проблематично по причинам, описанным Марчином в комментариях.Соответственно, при таком сценарии лучше всего избегать такого рода соединения , если это возможно.
В конечном итоге я решил эту проблему: очистить таблицу visitor_customer_hist и написать пользовательскую оконную функцию / udtf.
Изначально я создал lkp.visitor_customer_hist
таблицу, потому что она моглабыть созданы с использованием существующих оконных функций, и в базе данных SQL не-MPP могут быть созданы соответствующие индексы, которые сделают поиск достаточно производительным.Он был создан так:
CREATE TABLE lkp.visitor_customer_hist AS
SELECT
a.visitor_id AS visitor_id,
a.customer_id AS customer_id,
nvl(lag(a.session_datetime) OVER ( PARTITION BY a.visitor_id
ORDER BY a.session_datetime ), '1900-01-01') AS from_datetime,
CASE WHEN lead(a.session_datetime) OVER ( PARTITION BY a.visitor_id
ORDER BY a.session_datetime ) IS NULL THEN '9999-12-31'
ELSE a.session_datetime END AS to_datetime
FROM (
SELECT
s.session_id,
vs.visitor_id,
customer_id,
row_number() OVER ( PARTITION BY vs.visitor_id, s.session_datetime
ORDER BY s.session_id ) AS rn,
lead(s.customer_id) OVER ( PARTITION BY vs.visitor_id
ORDER BY s.session_datetime ) AS next_cust_id,
session_datetime
FROM "session" s
JOIN "visitor_session" vs ON vs.session_id = s.session_id
WHERE s.customer_id <> -2
) a
WHERE (a.next_cust_id <> a.customer_id
OR a.next_cust_id IS NULL) AND a.rn = 1;
Итак, отбрасывая этот подход , я написал следующую вставку UDTF:
CREATE OR REPLACE FUNCTION udtf_eff_customer(customer_id FLOAT)
RETURNS TABLE(effective_customer_id FLOAT)
LANGUAGE JAVASCRIPT
IMMUTABLE
AS '
{
initialize: function() {
this.customer_id = -1;
},
processRow: function (row, rowWriter, context) {
if (row.CUSTOMER_ID != -1) {
this.customer_id = row.CUSTOMER_ID;
}
rowWriter.writeRow({EFFECTIVE_CUSTOMER_ID: this.customer_id});
},
finalize: function (rowWriter, context) {/*...*/},
}
';
И его можно применить так:
SELECT
iff(a.customer_id <> -1, a.customer_id, ec.effective_customer_id) AS customer_id,
a.session_id
FROM "session" a
JOIN table(udtf_eff_customer(nvl2(a.visitor_id, a.customer_id, NULL) :: DOUBLE) OVER ( PARTITION BY a.visitor_id
ORDER BY a.session_datetime DESC )) ec
Таким образом, это приводит к желаемому результату: для каждого сеанса, если customer_id не является «неизвестным», тогда мы идем дальше и используем это;в противном случае мы используем следующий customer_id (если он существует), который можно связать с этим посетителем (упорядочение по времени сеанса).
Это гораздо лучшее решение, чем создание таблицы поиска;по сути, он занимает всего один проход для данных, требует гораздо меньше кода / сложности и идет очень быстро.