Используйте условие в подзапросе WHERE в postgresql

0

Мне нужна помощь в этой ситуации. У меня есть таблица с 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 в других). Даже если в продаже имеется более одного дубликата счета, я просто хочу знать, была ли продажа или нет

  • 0
    ваша продажа на основе электронной почты?
  • 0
    Да, каждое электронное письмо имеет либо 0 продаж, либо 1 продажу в одном из дублирующих аккаунтов. Я не верю, что есть случаи с продажами в двух или более дублирующих аккаунтах, основанных на одном и том же электронном письме, но если это произойдет, мне просто важно, была ли электронная почта в продаже или нет
Теги:
where

1 ответ

1

Коррелированный подзапрос можно переписать

  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
  • 0
    Спасибо, что ответили. В этом случае мне придется объединиться с dm1 и dm2, верно?
  • 0
    Первая часть моего ответа (первоначальный ответ) была предназначена для замены коррелированного подзапроса в скобках исходного запроса. Обновлен ответ, добавив демонстрацию исходного запроса с заменой, а также добавив альтернативный подход ... получение минимальной отметки времени для всей электронной почты в виде встроенного представления и объединение для получения строк, соответствующих этой минимальной отметке времени ,
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню