我一直在使用CTE阅读以下Microsoft article关于递归查询的内容,而且似乎无法解决如何将其用于群组常用项目。
我有一个包含以下列的表:
我需要做的是从表格中的第一个人开始,遍历表格并查找具有相同(LastName
和BirthCountry
)或具有相同(DateOfBirth
的所有人。 {1}}和BirthCountry
)。
现在棘手的部分是我必须为它们分配相同的GroupID
,然后对于GroupID
中的每个人,我需要查看其他人是否有相同的信息然后放入相同的GroupID
。
我认为我可以使用多个游标执行此操作,但它变得棘手。
以下是示例数据和输出。
ID FirstName LastName DateOfBirth BirthCountry GroupID
----------- ---------- ---------- ----------- ------------ -----------
1 Jonh Doe 1983-01-01 Grand 100
2 Jack Stone 1976-06-08 Grand 100
3 Jane Doe 1982-02-08 Grand 100
4 Adam Wayne 1983-01-01 Grand 100
5 Kay Wayne 1976-06-08 Grand 100
6 Matt Knox 1983-01-01 Hay 101
John Doe和Jane Doe属于同一组(100),因为它们具有相同的(LastName和BirthCountry)。
Adam Wayne在Group(100),因为他和John Doe一样(BirthDate和BirthCountry)。
Kay Wayne在Group(100)中,因为她与已经在Group(100)中的Adam Wayne具有相同的(LastName和BirthCountry)。
Matt Knox是一个新组(101),因为他与以前组中的任何人都不匹配。
Jack Stone属于一个团体(100),因为他和Kay Wayne一样(BirthDate和BirthCountry)已经在Group(100)。
数据脚本:
CREATE TABLE #Tbl(
ID INT,
FirstName VARCHAR(50),
LastName VARCHAR(50),
DateOfBirth DATE,
BirthCountry VARCHAR(50),
GroupID INT NULL
);
INSERT INTO #Tbl VALUES
(1, 'Jonh', 'Doe', '1983-01-01', 'Grand', NULL),
(2, 'Jack', 'Stone', '1976-06-08', 'Grand', NULL),
(3, 'Jane', 'Doe', '1982-02-08', 'Grand', NULL),
(4, 'Adam', 'Wayne', '1983-01-01', 'Grand', NULL),
(5, 'Kay', 'Wayne', '1976-06-08', 'Grand', NULL),
(6, 'Matt', 'Knox', '1983-01-01', 'Hay', NULL);
答案 0 :(得分:1)
我认为groupid是你想要的输出,从100开始。 即使groupid来自另一个表,那也没问题。
首先,抱歉我的“没有游标评论”。此任务需要进行追踪或RBAR操作。事实上经过很长一段时间后我遇到了这么长时间的需求并使用了RBAR操作。
如果明天我可以使用SET BASE METHOD,那么我会来编辑它。
最重要的是使用RBAR操作可以让脚本更加理解,我认为它也适用于其他样本数据。 还提供有关性能及其与其他样本数据一起使用的反馈。
Alsi在我的脚本中你注意到id不是串行的,没关系,我这样做是为了测试。
我使用print进行debuging目的,你可以删除它。
SET NOCOUNT ON
DECLARE @Tbl TABLE(
ID INT,
FirstName VARCHAR(50),
LastName VARCHAR(50),
DateOfBirth DATE,
BirthCountry VARCHAR(50),
GroupID INT NULL
);
INSERT INTO @Tbl VALUES
(1, 'Jonh', 'Doe', '1983-01-01', 'Grand', NULL) ,
(2, 'Jack', 'Stone', '1976-06-08', 'Grand', NULL),
(3, 'Jane', 'Doe', '1982-02-08', 'Grand', NULL),
(4, 'Adam', 'Wayne', '1983-01-01', 'Grand', NULL),
(5, 'Kay', 'Wayne', '1976-06-08', 'Grand', NULL),
(6, 'Matt', 'Knox', '1983-01-01', 'Hay', NULL),
(7, 'Jerry', 'Stone', '1976-06-08', 'Hay', NULL)
DECLARE @StartGroupid INT = 100
DECLARE @id INT
DECLARE @Groupid INT
DECLARE @Maxid INT
DECLARE @i INT = 1
DECLARE @MinGroupID int=@StartGroupid
DECLARE @MaxGroupID int=@StartGroupid
DECLARE @LastGroupID int
SELECT @maxid = max(id)
FROM @tbl
WHILE (@i <= @maxid)
BEGIN
SELECT @id = id
,@Groupid = Groupid
FROM @Tbl a
WHERE id = @i
if(@Groupid is not null and @Groupid<@MinGroupID)
set @MinGroupID=@Groupid
if(@Groupid is not null and @Groupid>@MaxGroupID)
set @MaxGroupID=@Groupid
if(@Groupid is not null)
set @LastGroupID=@Groupid
UPDATE A
SET groupid =case
when @id=1 and b.groupid is null then @StartGroupid
when @id>1 and b.groupid is null then @MaxGroupID+1--(Select max(groupid)+1 from @tbl where id<@id)
when @id>1 and b.groupid is not null then @MinGroupID --(Select min(groupid) from @tbl where id<@id)
end
FROM @Tbl A
INNER JOIN @tbl B ON b.id = @ID
WHERE (
(
a.BirthCountry = b.BirthCountry
and a.DateOfBirth = b.dateofbirth
)
or (a.LastName = b.LastName and a.BirthCountry = b.BirthCountry)
or (a.LastName = b.LastName and a.dateofbirth = b.dateofbirth)
)
--if(@id=7) --@id=2,@id=3 and so on (for debug
--break
SET @i = @i + 1
SET @ID = @I
END
SELECT *
FROM @Tbl
备用方法,但仍然返回56,000行而没有rownum = 1.请查看它是否与其他样本数据一起使用或查看是否可以进一步优化它。
;with CTE as
(
select a.ID,a.FirstName,a.LastName,a.DateOfBirth,a.BirthCountry
,@StartGroupid GroupID
,1 rn
FROM @Tbl A where a.id=1
UNION ALL
Select a.ID,a.FirstName,a.LastName,a.DateOfBirth,a.BirthCountry
,case when ((a.BirthCountry = b.BirthCountry and a.DateOfBirth = b.dateofbirth)
or (a.LastName = b.LastName and a.BirthCountry = b.BirthCountry)
or (a.LastName = b.LastName and a.dateofbirth = b.dateofbirth)
) then b.groupid else b.groupid+1 end
, b.rn+1
FROM @tbl A
inner join CTE B on a.id>1
where b.rn<@Maxid
)
,CTE1 as
(select * ,row_number()over(partition by id order by groupid )rownum
from CTE )
select * from cte1
where rownum=1
答案 1 :(得分:1)
这是我想出的。我很少编写递归查询,所以这对我来说是一个很好的做法。顺便说一句,Kay和Adam不会在您的样本数据中共享出生国家。
with data as (
select
LastName, DateOfBirth, BirthCountry,
row_number() over (order by LastName, DateOfBirth, BirthCountry) as grpNum
from T group by LastName, DateOfBirth, BirthCountry
), r as (
select
d.LastName, d.DateOfBirth, d.BirthCountry, d.grpNum,
cast('|' + cast(d.grpNum as varchar(8)) + '|' as varchar(1024)) as equ
from data as d
union all
select
d.LastName, d.DateOfBirth, d.BirthCountry, r.grpNum,
cast(r.equ + cast(d.grpNum as varchar(8)) + '|' as varchar(1024))
from r inner join data as d
on d.grpNum > r.grpNum
and charindex('|' + cast(d.grpNum as varchar(8)) + '|', r.equ) = 0
and (d.LastName = r.LastName or d.DateOfBirth = r.DateOfBirth)
and d.BirthCountry = r.BirthCountry
), g as (
select LastName, DateOfBirth, BirthCountry, min(grpNum) as grpNum
from r group by LastName, DateOfBirth, BirthCountry
)
select t.*, dense_rank() over (order by g.grpNum) + 100 as GroupID
from T as t
inner join g
on g.LastName = t.LastName
and g.DateOfBirth = t.DateOfBirth
and g.BirthCountry = t.BirthCountry
对于终止它的递归,必须跟踪等价(通过字符串连接),以便在每个级别它只需要考虑新发现的等价(或连接,转换等)。请注意我和#39;我避免使用 group 这个词来避免出现GROUP BY
概念。
http://rextester.com/edit/TVRVZ10193
编辑:我对等价使用了几乎任意的编号,但是如果你希望它们出现在基于最低ID的序列中,每个块都很容易做到。当然,row_number()
假设min(ID) as grpNum
不是ID
,而是import UIKit
public class MyController: UIViewController {
var completionHandler: ((Bool, String) -> Void)? // Bool and String are the return type, you can choose anything you want to return
public func onComplete(completion : ((Bool, String) -> Void)?)
{
self.completionHandler = completion
}
override public func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override public func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
是唯一的。
答案 2 :(得分:-1)
也许你可以用这种方式运行它
SELECT *
FROM table_name
GROUP BY
FirstName,
LastName,
GroupID
HAVING COUNT(GroupID) >= 2
ORDER BY GroupID