Используйте условие в подзапросе WHERE в postgresql - PullRequest
0 голосов
/ 23 мая 2018

Мне нужна помощь в этой ситуации.У меня есть таблица с UUID (уникальным), электронной почтой (повторяется), отметкой времени (уникальным) и has_sales (может быть 1, если да, и 0, если нет)

Пример данных

uuid       email       timestamp        has_sales
    1   a@gmail.com 2016-10-02 10:28:23    0
    2   a@gmail.com 2017-10-03 10:28:23    0
    3   a@gmail.com 2017-10-06 17:08:15    1
    4   a@gmail.com 2017-12-04 20:47:17    0
    5   a@gmail.com 2018-05-21 15:27:04    0
    6   b@gmail.com 2016-10-02 10:28:23    1
    7   b@gmail.com 2017-10-03 10:28:23    0

Iхотел бы выбрать самую старую временную метку, кроме случаев, когда есть продажа на более новой (редко, но это может произойти).Итак, ожидаемый результат будет

  uuid     email       timestamp        has_sales
    3   a@gmail.com 2017-10-06 17:08:15    1
    6   b@gmail.com 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 в других).Даже если есть несколько дублирующих аккаунтов с продажами, я просто хочу знать, была ли продажа или нет

1 Ответ

0 голосов
/ 23 мая 2018

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

  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
...