【问题标题】:Row numbers using group by and several columns使用 group by 和几列的行号
【发布时间】:2013-05-17 18:01:00
【问题描述】:

为了在网格中显示数据,我必须构建 SQL 请求时遇到了一些问题。

我有如下的临时表(例如,我输入数字而不是电子邮件地址):

GroupID | Email1 | Email2 
null    |   1    | 2
null    |   1    | 2
null    |   1    | null
null    |   3    | 1
null    |   2    | 2
null    |   4    | 2
null    |   5    | 6
null    |   6    | null

我需要更新表格以设置 GroupID,如下所述: 如果 email1 或 email2 匹配任何其他记录,则此记录需要与其他记录具有相同的 groupId。例如(使用上表):

GroupID | Email1 | Email2
**1**   |   1    | 2
**1**   |   1    | 2
**1**   |   1    | null
**1**   |   3    | 1
**1**   |   2    | 2
**1**   |   4    | 2
**2**   |   5    | 6
**2**   |   6    | null

我尝试过类似的方法:

UPDATE a
SET a.GroupId = b.GroupId
FROM   #temp a
INNER JOIN (SELECT Email, 
                   ROW_NUMBER() OVER (ORDER BY ISNULL(Email,'zzzzzzzz')) GroupId
            FROM (SELECT Email1 Email
                  FROM #temp
                  GROUP BY Email1
                  UNION ALL 
                  SELECT Email2 Email   
                  FROM #temp                
                  GROUP BY Email2 
                 ) c
            GROUP BY Email
           ) b
ON a.Email1 = b.Email OR
   a.Email2 = b.Email OR 
   (b.Email IS NULL AND a.Email1 IS NULL AND a.Email2 IS NULL)

但这不起作用我打算...例如,Email2等于Email 1的情况不被识别为同一组... 我怎样才能使这个请求按我的意愿工作?有可能吗??

[EDIT] 2013/15/17 14:15 :实际上,对于规则,我的意思是“如果 email1 或 email 2 与任何其他记录的 email1 或 email2 匹配,则应该属于相同的 groupID”

【问题讨论】:

  • 我认为规则不完整:“如果 email1 或 email2 匹配任何其他记录,则此记录需要与其他记录具有相同的 groupId”。根据这条规则,给所有行赋予相同的 GroupID 是有效的。这更简单,更容易实现,但可能不是您需要的。那你还需要什么?
  • 这是哪个 RDBMS(SQLite、SQLServer、MySQL 等)?

标签: sql sql-server-2008


【解决方案1】:

这不能在单个JOIN 中执行,因为可能需要遍历很长的电子邮件链,例如1, 2 -> 2, 3 -> 3, 4 -> ... -> 99, 100。 (您可以使用递归 CTE 在单个语句中执行此操作——以某种方式解决 GROUP BY 问题——但你知道我的意思。)

这是一种方法(SQL Server 2005 及更高版本):

WITH E AS (
   SELECT
      Num = Row_Number() OVER (ORDER BY (SELECT 1)),
      *
   FROM dbo.EmailGroups
)
UPDATE E
SET E.GroupID = E.Num
;

WHILE @@RowCount > 0 BEGIN
   UPDATE E
   SET E.GroupID = X.MinGroupID
   FROM
      dbo.EmailGroups E
      INNER JOIN (
         SELECT
            E1.GroupID,
            MinGroupID = Min(E2.GroupID)
         FROM
            dbo.EmailGroups E1
            INNER JOIN dbo.EmailGroups E2
               ON E1.Email1 IN (E2.Email1, E2.Email2)
               OR E1.Email2 IN (E2.Email1, E2.Email2)
         GROUP BY
            E1.GroupID
         HAVING
            E1.GroupID <> Min(E2.GroupID)
      ) X ON E.GroupID = X.GroupID
   ;
END;

See this working in a SQL Fiddle.

这将导致每个链接的行集具有相同的GroupID,与所有其他GroupIDs 不同(但它们不会是连续的,会有间隙)。如果您需要它们是连续的,请执行最终更新以将 GroupID 设置为 DENSE_RANK() OVER (ORDER BY GroupID) - 这在 Fiddle 中显示。

【讨论】:

  • 。 .注意in 和NULL 值。我认为您的查询有效,但您要确保不遵循 NULL 链。而且,如果一个值为 NULL,您确实希望确保匹配任一值。
  • 哦,是的,我使用的是 sql server 2008。抱歉耽搁了!
  • @GordonLinoff 除非SET ANSI_NULLSOFF,否则一切都很好。
  • 是的,这很好用!谢谢你的帮助。我不认为我会自己发现...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-30
  • 2021-07-08
  • 1970-01-01
相关资源
最近更新 更多