在postgresql的where子查询中使用条件

oxalkeyp  于 2021-06-21  发布在  Mysql
关注(0)|答案(1)|浏览(485)

我想在这种情况下得到一些帮助。我有一个带有uuid(unique)、email(repeated)、timestamp(unique)的表,并且有\u sales(如果是可以是1,如果否可以是0)
样本数据

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

我想选择最老的时间戳,除非有一个较新的销售(这是罕见的,但它可能发生)。所以,预期的结果是

  1. uuid email timestamp has_sales
  2. 3 a@gmail.com 2017-10-06 17:08:15 1
  3. 6 b@gmail.com 2016-10-02 10:28:23 1

目前,我只使用第一个条件(最早的时间戳),如下所示:

  1. SELECT
  2. dm1.uuid,
  3. dm1.email,
  4. dm1.timestamp,
  5. dm1.has_sales
  6. FROM dup_mail dm1
  7. where
  8. time_stamp = (select min(time_stamp)
  9. from dup_mail dm2
  10. where dm1.email = dm2.email
  11. )
  12. order by 2

如何升级这个代码,我可以添加一个条件,如果有销售给一个较新的用户,而没有销售给旧的,我会选择一个较新的?每封电子邮件都与“无销售”(所有重复帐户中为0)或“是销售”(其中一个重复帐户中为1,其他帐户中为0)相关。即使有一个以上的重复帐户与销售,我只想知道是否有销售或没有

mu0hgdu0

mu0hgdu01#

相关子查询可以重写

  1. SELECT dm2.timestamp
  2. FROM dup_mail dm2
  3. WHERE dm2.email = dm1.email
  4. ORDER
  5. BY dm2.has_sales DESC
  6. , dm2.timestamp ASC
  7. LIMIT 1

这将对行进行排序 has_sales=1 在具有的行之前 has_sales=0 ,然后 timestamp . 这个 LIMIT 1 子句选择第一行(在对集合排序之后)
我们需要一个合适的索引 dup_mail 带的表格 email 作为第一列。包括 timestamp 以及 has_sales 索引中的列将使其成为子查询的覆盖索引。
这应该满足规范,但是相关子查询在性能方面可能不是最优的。

  1. SELECT dm1.uuid
  2. , dm1.email
  3. , dm1.timestamp
  4. , dm1.has_sales
  5. FROM dup_mail dm1
  6. WHERE dm1.timestamp =
  7. ( SELECT dm2.timestamp
  8. FROM dup_mail dm2
  9. WHERE dm2.email = dm1.email
  10. ORDER
  11. BY dm2.has_sales DESC
  12. , dm2.timestamp ASC
  13. LIMIT 1
  14. )
  15. ORDER
  16. BY ...

(时间戳在所有行中都是唯一的,这有点奇怪;但如果是这样,那么这个查询就可以工作了。)
我们可以通过以下方式获得更好的性能:

  1. SELECT dmx.email
  2. , IF( MAX(dmx.has_sales)=0
  3. , MIN(dmx.timestamp)
  4. , MIN(IF(dmx.has_sales=1,dmx.timestamp,NULL))
  5. ) AS min_timestamp
  6. FROM dup_email dmx
  7. GROUP BY dmx.email

然后将其用作内联视图并连接到 dup_mail 表以获取与最小时间戳关联的行

  1. SELECT dm1.uuid
  2. , dm1.email
  3. , dm1.timestamp
  4. , dm1.has_sales
  5. FROM ( -- minimum timestamp for each email
  6. SELECT dmx.email
  7. , IF( MAX(dmx.has_sales)=0
  8. , MIN(dmx.timestamp)
  9. , MIN(IF(dmx.has_sales=1,dmx.timestamp,NULL))
  10. ) AS min_timestamp
  11. FROM dup_email dmx
  12. GROUP BY dmx.email
  13. ) m
  14. JOIN dup_email dm1
  15. ON dm1.email = m.email
  16. AND dm1.timestamp = m.min_timestamp
  17. ORDER
  18. BY ...

笔记
上面给出的sql语法是特定于mysql的(问题被标记为mysql)。
我认为 IF() 函数是仅限mysql的扩展。
对于postgresql,替换为:

  1. , IF( MAX(dmx.has_sales)=0
  2. , MIN(dmx.timestamp)
  3. , MIN(IF(dmx.has_sales=1,dmx.timestamp,NULL))
  4. ) AS min_timestamp

更便携,更符合ansi标准

  1. , CASE WHEN MAX(dmx.has_sales) = 0
  2. THEN MIN(dmx.timestamp)
  3. ELSE MIN( CASE WHEN dmx.has_sales = 1
  4. THEN dmx.timestamp
  5. END
  6. )
  7. END AS min_timestamp
展开查看全部

相关问题