我想在这种情况下提供一些帮助。我有一个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个)相关。即使有多个重复的帐户与销售,我只想知道是否有销售
答案 0 :(得分:1)
可以重写相关子查询
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