行号使用group by和几列

时间:2013-05-17 18:01:00

标签: sql sql-server-2008

我在编写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)

但这不符合我的意图...例如,电子邮件1等于电子邮件1的情况不被视为同一组... 我怎么能按照我的意愿提出这个要求呢?它甚至可能吗?

[编辑] 2013/15/17 14:15:根据规则,我的意思是“如果电子邮件1或电子邮件2与任何其他记录的电子邮件1或电子邮件2匹配,则应该是相同的组ID”

1 个答案:

答案 0 :(得分:4)

这不能在单个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) - 这将显示在小提琴中。