I added start and end dates to table GroupMembers and added StartDate to the primary key. This is to track when an individual or groups enters/exits a group. I embedded the group derivation logic in a function which accepts two parameters:@GroupCode and @GroupDate. I added some additional test cases. Everything is working as expected. I just wanted to post a follow up in case anyone saw any issues with the code or thought it could be useful to them. Thanks again Drew.
--Drop tables if they exist
IF OBJECT_ID('[dbo].[GroupMembers]') IS NOT NULL DROP TABLE dbo.GroupMembers
IF OBJECT_ID('[dbo].[Groups]') IS NOT NULL DROP TABLE dbo.Groups
GO
--Create tables
CREATE TABLE dbo.Groups
(
GroupCode INT NOT NULL,
GroupDescription VARCHAR(40) NULL
CONSTRAINT PK_Groups PRIMARY KEY CLUSTERED (GroupCode ASC)
)
CREATE TABLE dbo.GroupMembers(
GroupCode INT NOT NULL,
Member INT NOT NULL,
MemberType VARCHAR(12) NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NULL,
CONSTRAINT PK_GroupMembers PRIMARY KEY CLUSTERED
(
GroupCode ASC,
Member ASC,
MemberType ASC,
StartDate
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
)
GO
ALTER TABLE dbo.GroupMembers WITH CHECK ADD CONSTRAINT FK_GroupMembers_Groups FOREIGN KEY(GroupCode)
REFERENCES dbo.Groups (GroupCode)
GO
--Drop function if exists
IF OBJECT_ID('GetGroupMembers') IS NOT NULL DROP FUNCTION GetGroupMembers
GO
--Create function
CREATE FUNCTION dbo.GetGroupMembers
(
@GroupCode INT,
@GroupDate DATE
)
RETURNS TABLE AS
RETURN
WITH Members AS
(
--Anchor
SELECT
GroupCode,
Member
FROM GroupMembers
WHERE
@GroupDate BETWEEN StartDate AND COALESCE(EndDate,GETDATE()) AND
MemberType = 'Individual'
UNION ALL
--Recursive call
SELECT
h.GroupCode,
m.Member
FROM GroupMembers h
INNER JOIN Members m
ON h.Member = m.GroupCode
WHERE
@GroupDate BETWEEN StartDate AND COALESCE(EndDate,GETDATE()) AND
h.MemberType = 'Group'
)
SELECT
Member
FROM Members m
WHERE
@GroupCode = GroupCode
GO
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (1,'Simple group')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (2,'Simple group')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (3,'Group of Groups')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (4,'Mixed')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (5,'Complicated')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (6,'Different Start Dates')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (7,'Different Start Dates and an End Date')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (8,'additional test group')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (9,'additional test group')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (10,'additional test group')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (11,'additional test group')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (12,'additional test group')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (13,'additional test group')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (14,'additional test group')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (15,'additional test group')
INSERT INTO Groups (GroupCode,GroupDescription) VALUES (16,'additional test group')
--Group 1 has two individual members; no nested groups
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (1,1,'Individual','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (1,2,'Individual','2018-02-01',NULL)
--Group 2 has two individual members; no nested groups
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (2,3,'Individual','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (2,4,'Individual','2018-02-01',NULL)
--Group 3 has two members both of which are groups
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (3,1,'Group','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (3,2,'Group','2018-02-01',NULL)
--Group 4 has an individual and a group
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (4,1,'Group','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (4,4,'Individual','2018-02-01',NULL)
--Group 5 has an individual and a group; The group is a nested group
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (5,3,'Group','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (5,5,'Individual','2018-02-01',NULL)
DECLARE
@GroupCode INT,
@GroupDate DATE
--GroupCode = 1; GroupDate = '2018-02-15'
SET @GroupCode = 1
SET @GroupDate = '2018-02-15'
SELECT
'GroupCode = 1; GroupDate = 2018-02-15' AS Test,
Member,
'Expected results: Members 1 and 2' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--GroupCode = 2; GroupDate = '2018-02-15'
SET @GroupCode = 2
SET @GroupDate = '2018-02-15'
SELECT
'GroupCode = 2; GroupDate = 2018-02-15' AS Test,
Member,
'Expected results: Members 3,4' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--GroupCode = 3; GroupDate = '2018-02-15'
SET @GroupCode = 3
SET @GroupDate = '2018-02-15'
SELECT
'GroupCode = 3; GroupDate = 2018-02-15' AS Test,
Member,
'Expected results: 1,2,3,4' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--GroupCode = 4; ; GroupDate = '2018-02-15'
SET @GroupCode = 4
SET @GroupDate = '2018-02-15'
SELECT
'GroupCode = 4; ; GroupDate = 2018-02-15' AS Test,
Member,
'Expected results: 1,2,4' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--GroupCode = 5; GroupDate = current date
SET @GroupCode = 5
SET @GroupDate = GETDATE()
SELECT
'GroupCode = 5; GroupDate = current date' AS Test,
Member,
'Expected results: 1,2,3,4,5' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--Start testing the start and end date logic
--Create new groups
--Group 6 has two members both of which are groups; Group 1 joined on 2018-02-01 and Group 2 joined on 2018-02-02
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (6,1,'Group','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (6,2,'Group','2018-02-02',NULL)
--GroupCode = 6; GroupDate = '2018-02-15';
SET @GroupCode = 6
SET @GroupDate = '2018-02-15'
SELECT
'GroupCode = 6; GroupDate = 2018-02-15' AS Test,
Member,
'Expected results: The individual members of both groups 1 and 2: 1,2,3,4. All existed in the group on 2018-02-15' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--GroupCode = 6; GroupDate = '2018-02-15';
SET @GroupCode = 6
SET @GroupDate = '2018-02-15'
SELECT
'GroupCode = 6; GroupDate = 2018-02-15' AS Test,
Member,
'Expected results: The individual members of both groups 1 and 2; 1,2,3,4 All existed in the group on 2018-02-02' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--GroupCode = 6; GroupDate = '2018-02-01';
SET @GroupCode = 6
SET @GroupDate = '2018-02-01'
SELECT
'GroupCode = 6; GroupDate = 2018-02-01' AS Test,
Member,
'Expected results: The individual members of group 1 ; Members 1,2; Only group 1 existed in the group on 2018-02-01' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--GroupCode = 6; GroupDate = '2017-02-01';
SET @GroupCode = 6
SET @GroupDate = '2017-02-01'
SELECT
'GroupCode = 6; GroupDate = 2017-02-01' AS Test,
Member,
'Expected results: None; No groups existed on 2017-02-01' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--Group 7; Sames as group 6 but group 1 exited the group on 2018-02-05
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (7,1,'Group','2018-02-01','2018-02-05')
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (7,2,'Group','2018-02-02',NULL)
--GroupCode = 7; GroupDate = '2018-02-05';
SET @GroupCode = 7
SET @GroupDate = '2018-02-05'
SELECT
'GroupCode = 7; GroupDate = 2018-02-05' AS Test,
Member,
'Expected results: The individual members of both groups 1 and 2; 1,2,3,4 All existed in the group on 2018-02-05' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--GroupCode = 7; GroupDate = '2018-02-06';
SET @GroupCode = 7
SET @GroupDate = '2018-02-06'
SELECT
'GroupCode = 7; GroupDate = 2018-02-06' AS Test,
Member,
'Expected results: 3 and 4; Group 1 exited group 7 on 2018-02-05' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--GroupCode = 7; GroupDate = '2018-02-01'
SET @GroupCode = 7
SET @GroupDate = '2018-02-01'
SELECT
'GroupCode = 7; GroupDate = 2018-02-01' AS Test,
Member,
'Expected results: The individual members of group 1; Group 2 joined group 7 the next day' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--Nest deeper
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (8,9,'Group','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (9,10,'Group','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (10,11,'Group','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (11,1,'Individual','2018-02-01',NULL)
--GroupCode = 8; GroupDate = '2018-02-01';
SET @GroupCode = 8
SET @GroupDate = '2018-02-01'
SELECT
'GroupCode = 8; GroupDate = 2018-02-01' AS Test,
Member,
'Expected results: Member 1; Group 8 contains group 9 which contains group 10 which contains group 11; Group 11 contains the individual member 1' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--Testing group 9, 10 and 11 will get the same result
SET @GroupCode = 11
SET @GroupDate = '2018-02-01'
SELECT
'GroupCode = 11; GroupDate = 2018-02-01' AS Test,
Member,
'Expected results: Member 1' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--Test when an intermediate nested group has an individual member
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (12,13,'Group','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (13,14,'Group','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (13,8,'Individual','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (14,15,'Group','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (15,1,'Individual','2018-02-01',NULL)
--GroupCode = 12; GroupDate = '2018-02-01';
SET @GroupCode = 12
SET @GroupDate = '2018-02-01'
SELECT
'GroupCode = 12; GroupDate = 2018-02-01' AS Test,
Member,
'Expected results: 1 and 8; Group 12 contains group 13 which contains group 14 (and individual 8) which contains group 15; Group 15 contains the member 1' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--A group exits and re-enters a group; Has an individual from the start
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (16,19,'Individual','2018-02-01',NULL)
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (16,1,'Group','2018-02-01','2018-02-05')
INSERT INTO GroupMembers (GroupCode,Member,MemberType,StartDate,EndDate) VALUES (16,1,'Group','2018-02-10','2018-02-15')
--GroupCode = 16; GroupDate = '2018-02-05';
SET @GroupCode = 16
SET @GroupDate = '2018-02-05'
SELECT
'GroupCode = 16; GroupDate = 2018-02-05' AS Test,
Member,
'Expected results: Members 1, 2 and 19; Group 1 which contains members 1 and 2 was in group 16 on 2018-02-05' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--GroupCode = 16; GroupDate = '2018-02-09';
SET @GroupCode = 16
SET @GroupDate = '2018-02-09'
SELECT
'GroupCode = 16; GroupDate = 2018-02-09' AS Test,
Member,
'Expected results: Just 19; Group 1 which contains members 1 and 2 was not in group 16 on 2018-02-09' AS ExpectedResults
FROM dbo.GetGroupMembers(@GroupCode,@GroupDate)
--View the group and group members
--SELECT * FROM dbo.Groups
--SELECT * FROM dbo.GroupMembers