对于这种情况我需要一些帮助。我有一个表,其中包含 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
作为前导列。在索引中包含 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
然后将其用作内联 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/