Мне нужна помощь в этой ситуации. У меня есть таблица с UUID (уникальная), электронная почта (повторенная), временная метка (уникальная) и has_sales (может быть 1, если да и 0, если нет)
Пример данных
uuid email timestamp has_sales
1 [email protected] 2016-10-02 10:28:23 0
2 [email protected] 2017-10-03 10:28:23 0
3 [email protected] 2017-10-06 17:08:15 1
4 [email protected] 2017-12-04 20:47:17 0
5 [email protected] 2018-05-21 15:27:04 0
6 [email protected] 2016-10-02 10:28:23 1
7 [email protected] 2017-10-03 10:28:23 0
Я хотел бы выбрать самую старую временную метку, за исключением случаев, когда есть продажа на более новой (это редко, но это может произойти). Таким образом, ожидаемый результат
uuid email timestamp has_sales
3 [email protected] 2017-10-06 17:08:15 1
6 [email protected] 2016-10-02 10:28:23 1
В настоящее время я использую первое условие (старую временную метку) следующим образом:
SELECT
dm1.uuid,
dm1.email,
dm1.timestamp,
dm1.has_sales
FROM dup_mail dm1
where
time_stamp = (select min(time_stamp)
from dup_mail dm2
where dm1.email = dm2.email
)
order by 2
Как, обновив этот код, могу ли я добавить условие, если есть продажа для нового пользователя и не продается старшему, я бы выбрал более новый? Каждое электронное письмо связано либо с продажей (0 во всех дублированных учетных записях), либо с продажей (1 в одном из дубликатов и 0 в других). Даже если в продаже имеется более одного дубликата счета, я просто хочу знать, была ли продажа или нет
Коррелированный подзапрос можно переписать
SELECT dm2.timestamp
FROM dup_mail dm2
WHERE dm2.email = dm1.email
ORDER
BY dm2.has_sales DESC
, dm2.timestamp ASC
LIMIT 1
Это будет сортировать строки с has_sales=1
перед строками с has_sales=0
, а затем по timestamp
. Предложение LIMIT 1
выбирает первую строку (после сортировки набора).
Нам понадобится подходящий индекс в таблице dup_mail
с email
в качестве ведущего столбца. Включая столбцы timestamp
и has_sales
в индексе, он станет индексом покрытия для подзапроса.
Это должно удовлетворять спецификации, но коррелированный подзапрос может быть не оптимальным с точки зрения производительности.
SELECT dm1.uuid
, dm1.email
, dm1.timestamp
, dm1.has_sales
FROM dup_mail dm1
WHERE dm1.timestamp =
( SELECT dm2.timestamp
FROM dup_mail dm2
WHERE dm2.email = dm1.email
ORDER
BY dm2.has_sales DESC
, dm2.timestamp ASC
LIMIT 1
)
ORDER
BY ...
(Немного странно, что временная метка будет уникальной для всех строк, но если это так, тогда этот запрос может работать.)
Мы могли бы получить более высокую производительность с чем-то вроде этого:
SELECT dmx.email
, IF( MAX(dmx.has_sales)=0
, MIN(dmx.timestamp)
, MIN(IF(dmx.has_sales=1,dmx.timestamp,NULL))
) AS min_timestamp
FROM dup_email dmx
GROUP BY dmx.email
А затем используя это как встроенное представление и присоединитесь к таблице dup_mail
чтобы получить строки, связанные с минимальной меткой времени
SELECT dm1.uuid
, dm1.email
, dm1.timestamp
, dm1.has_sales
FROM ( -- minimum timestamp for each email
SELECT dmx.email
, IF( MAX(dmx.has_sales)=0
, MIN(dmx.timestamp)
, MIN(IF(dmx.has_sales=1,dmx.timestamp,NULL))
) AS min_timestamp
FROM dup_email dmx
GROUP BY dmx.email
) m
JOIN dup_email dm1
ON dm1.email = m.email
AND dm1.timestamp = m.min_timestamp
ORDER
BY ...
НОТА
Синтаксис SQL, приведенный выше, специфичен для MySQL (вопрос помечен как MySQL).
Я думаю, что функция IF()
является расширением MySQL.
Для PostgreSQL замените это:
, IF( MAX(dmx.has_sales)=0
, MIN(dmx.timestamp)
, MIN(IF(dmx.has_sales=1,dmx.timestamp,NULL))
) AS min_timestamp
с более портативными, более совместимыми с ANSI стандартами
, CASE WHEN MAX(dmx.has_sales) = 0
THEN MIN(dmx.timestamp)
ELSE MIN( CASE WHEN dmx.has_sales = 1
THEN dmx.timestamp
END
)
END AS min_timestamp