[英]Use a condition in a WHERE subquery in postgresql
在这种情况下,我需要一些帮助。 我有一个表,其中包含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
行进行has_sales=0
,然后对timestamp
进行排序。 LIMIT 1
子句选择第一行(对集合进行排序之后)。
我们将在dup_mail
表上希望有一个合适的索引, dup_mail
以email
为dup_mail
。 在索引中包含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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.