mysql - 在 postgresql 的 WHERE 子查询中使用条件

标签 mysql postgresql where-clause

对于这种情况我需要一些帮助。我有一个表,其中包含 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

我想选择最旧的时间戳,除非有较新的时间戳(这种情况很少见,但可能会发生)。所以,预期的结果是

  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 次)相关。即使有多个重复帐户有销售,我只想知道是否有销售

最佳答案

相关子查询可以重写

  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 的行之前,然后按时间戳 排序。 LIMIT 1 子句选择第一行(在集合排序之后)。

我们需要在 dup_mail 表上建立一个合适的索引,并以 email 作为前导列。在索引中包含 timestamphas_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

然后将其用作内联 View 并联接到 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

关于mysql - 在 postgresql 的 WHERE 子查询中使用条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50497024/

相关文章:

sql - 在 TimescaleDB 的gapfill 的WHERE 子句中使用子查询

mysql - 为什么多个 where 子句不起作用

Mysql:添加外键不会在 MyISAM 表上给出警告/错误

mysql - 存储过程不提供输出

postgresql - 如何在 PostgreSQL 的函数内传递变量

postgresql - 错误: cursor "<unnamed portal>" does not exist

mysql - 连接 ODK 聚合和 MySQL 服务器

mysql - SQL - 查询关联 3 个不同的表和一个弱实体

postgresql - PostgreSQL 中的时区转换不一致

mysql - 在一个查询中获取所有 WHERE 案例