【问题标题】:A work around Group By Clause Limitation解决按条款限制分组的方法
【发布时间】:2009-02-25 12:31:52
【问题描述】:

我正在开发一个社交网络 Web 应用程序,我遇到了一种情况,我需要向尚未激活电子邮件的用户重新发送提醒电子邮件。

问题是当我调查数据库时,我发现许多电子邮件是重复的(显然没有对电子邮件唯一性进行验证。所以我需要知道的是从数据库中检索这些字段的用户名、电子邮件激活码, 电子邮件,以便我可以重新发送激活电子邮件,对于重复的电子邮件,我只需要返回其中一个(即,如果我有用户 john 的电子邮件 john@hotmail.com 和用户 john1 的电子邮件 john@hotmail .com 也是,我只想检索这些 johns 中的一个(无论 john1 还是两个),所以我想通过(Group By Email)跟踪 SQL 查询。

问题是我无法选择不在 group by 子句中的其他字段。我在这里的解决方案是我不喜欢的;我创建了一个列表,每次需要向用户发送电子邮件时,我都会遍历整个列表以确保该电子邮件不存在,如果它不存在,我发送给它,然后将电子邮件添加到列表。
类似于以下内容:

if(!EmailIsInList(email)){ 
  SendActivationEmail(email);
  AddEmailToList(email)
}
else { DoNotSend); }

实际上我通过这种方式解决了问题,但我仍然不喜欢我的解决方案。有什么想法吗?

【问题讨论】:

  • 你能描述一下表结构吗?是否所有字段(用户名、电子邮件激活码、电子邮件、激活标志)都在同一个表中,或者它们位于不同的表中(例如用户表(UserId、UserName、Email、IsActivated)和电子邮件表(EmailId、UserId、日期、消息) ))?
  • 这是表格结构。表 1 用户:UserID pk Email RegisterationDate。表 2 UserActivation:ID pk、UserID fk 参考用户(UserID)、EmailActivated 位、Email 代码。现在我需要结果集如下用户ID - 电子邮件 - 电子邮件代码 - 用户名(没有多余的电子邮件)
  • 不知道为什么这是个问题。太多的电子邮件堵塞了您的系统?用户抱怨他们不想使用的帐户上的电子邮件?您希望根据电子邮件回复激活用户帐户。谁在乎两个用户是否共享一个电子邮件帐户?

标签: asp.net sql sql-server aggregate


【解决方案1】:

收入测试数据:

DECLARE @User TABLE (UserId int, 
UserName varchar(100), Email varchar(40), IsActivated bit)
INSERT INTO @User
SELECT 1, 'John', 'john@hotmail.com', 0 UNION
SELECT 2, 'Ann', 'ann@hotmail.com', 0 UNION
SELECT 3, 'John2', 'john@hotmail.com', 1 UNION
SELECT 4, 'Bill', 'bill@hotmail.com', 0 UNION
SELECT 5, 'Bill', 'john@hotmail.com', 0

DECLARE @Email TABLE (EmailId int, 
UserId int, Date datetime, Message varchar(1000))
INSERT INTO @Email
SELECT 1, 1, GETDATE(), '' UNION
SELECT 2, 2, GETDATE(), '' UNION
SELECT 3, 3, GETDATE(), '' UNION
SELECT 4, 4, GETDATE(), '' UNION
SELECT 5, 5, GETDATE(), ''

SELECT * FROM @User
SELECT * FROM @Email

你看,我们已经激活了一次 john@hotmail.com,所以我们不需要他在结果集中。
现在,使用 RANK OVER 实现:

SELECT M.UserID, M.UserName, M.Email, 
    M.IsActivated, M.EmailId, M.Date, M.Message 
FROM (
    SELECT RANK() OVER (PARTITION BY U.Email 
        ORDER BY U.IsActivated Desc, U.UserID ASC) AS N, 
        U.UserID, U.UserName, U.Email, U.IsActivated, 
        E.EmailId, E.Date, E.Message
    FROM @User U INNER JOIN @Email E ON U.UserID = E.UserID
)M WHERE M.N = 1 AND M.IsActivated = 0

【讨论】:

  • Bliek 在我的链接中提供的解决方案基本上没有区别 :) 但提供了一个无需思考的解决方案 :)
  • 嗯,实现完全不同 - 没有 CTE,没有 RowNumber。顺便说一句,“跨平台”解决方案:D
  • 我的意思是 :) 我喜欢这种“跨平台”解决方案!
【解决方案2】:

如果我们假设同一个电子邮件地址既可以针对 usera 激​​活,也可以针对 userb 不激活,那么以下查询将为您返回一个从未激活过的电子邮件地址的用户 ID

SELECT  MAX(userid),
        email
FROM    users AS u1
WHERE   activated = 'False'
AND NOT EXISTS (
        SELECT 1
        FROM   users AS u2
        WHERE  u2.email = u1.email
        AND    u2.activated = 'True'
        )

GROUP BY email

您确实希望确保电子邮件字段已编入索引,并且如果使用 (email, userid) 的唯一复合键对其进行索引,那么这将是索引扫描并且应该很快。

【讨论】:

  • 请阅读我对这个问题的评论,我在那里描述了表格结构!
【解决方案3】:

我认为你犯了一个重大的逻辑错误。电子邮件地址不是,也永远不会是唯一的。仅仅因为两个用户拥有相同的电子邮件地址并不意味着他们是同一个人!人们经常共享电子邮件,夫妻可能拥有相同的电子邮件,小型办公室有时只有一封电子邮件(这通常适用于医生办公室。)如果有人放弃,电子邮件也会被重复使用。因此,2007 年在 jsmith@hotmail.com 注册的 John Smith 在您的系统中可能还没有处于活动状态,因此当他访问 jsmith@gmail.com 时也没有费心去更改他的电子邮件。与此同时,Judy Smith 在不同的州注册 jsmith@hotmail.com。您不能假设电子邮件地址永远是唯一的。

【讨论】:

  • 是的,我明白了你的意思,这很有意义,如果我要重新设计数据库,我会这样说。但是我从我的前任那里继承了这个设计,我真的需要处理这种情况。
猜你喜欢
  • 2015-03-29
  • 1970-01-01
  • 1970-01-01
  • 2016-09-03
  • 1970-01-01
  • 2016-08-31
  • 1970-01-01
  • 1970-01-01
  • 2018-06-25
相关资源
最近更新 更多