我在编写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”
答案 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)
- 这将显示在小提琴中。