SQL Server 2012 以降を使用している場合は、SQL Server のウィンドウ関数を使用して、次のように目的の結果を得ることができます:
CREATE TABLE [dbo].[EventStates](
[Item] [varchar](1) NOT NULL,
[EventDate] [varchar](19) NOT NULL,
[Event] [varchar](3) NOT NULL
) ON [PRIMARY]
GO
INSERT [dbo].[EventStates] ([Item], [EventDate], [Event]) VALUES (N'A', N'2011-10-03 00:01:00', N'On')
INSERT [dbo].[EventStates] ([Item], [EventDate], [Event]) VALUES (N'A', N'2011-10-03 00:01:15', N'On')
INSERT [dbo].[EventStates] ([Item], [EventDate], [Event]) VALUES (N'B', N'2011-10-03 00:01:00', N'On')
INSERT [dbo].[EventStates] ([Item], [EventDate], [Event]) VALUES (N'A', N'2011-10-03 00:02:00', N'Off')
INSERT [dbo].[EventStates] ([Item], [EventDate], [Event]) VALUES (N'A', N'2011-10-03 00:02:01', N'Off')
INSERT [dbo].[EventStates] ([Item], [EventDate], [Event]) VALUES (N'C', N'2011-10-03 00:01:00', N'On')
INSERT [dbo].[EventStates] ([Item], [EventDate], [Event]) VALUES (N'B', N'2011-10-03 00:02:00', N'Off')
INSERT [dbo].[EventStates] ([Item], [EventDate], [Event]) VALUES (N'A', N'2011-10-03 00:02:02', N'On')
INSERT [dbo].[EventStates] ([Item], [EventDate], [Event]) VALUES (N'C', N'2011-10-03 00:02:05', N'On')
INSERT [dbo].[EventStates] ([Item], [EventDate], [Event]) VALUES (N'A', N'2011-10-03 00:02:07', N'Off')
GO
;WITH StateChange
AS
(
SELECT E.[Item]
,E.[EventDate]
,E.[Event]
,( -- First determine if a state change to on has occurred.
CASE
WHEN E.[Event] = 'On' AND LAG(E.[Event], 1, NULL) OVER (PARTITION BY E.[Item] ORDER BY E.[EventDate] ASC) IS NULL THEN 1
WHEN E.[Event] = 'On' AND E.[Event] <> LAG(E.[Event], 1, NULL) OVER (PARTITION BY E.[Item] ORDER BY E.[EventDate] ASC) THEN 1
ELSE 0
END
) [StateChanged]
FROM EventStates E
), StateChangeGrouping
AS
(
SELECT [Item]
,[EventDate]
,[Event]
,[StateChanged]
,SUM([StateChanged]) OVER (PARTITION BY [Item] ORDER BY [EventDate] ASC) AS [GroupID]
FROM StateChange
), StateChangeRanked
AS
(
SELECT [Item]
,[EventDate]
,[Event]
,[StateChanged]
,[GroupID]
,ROW_NUMBER() OVER (PARTITION BY [Item], GroupID, [Event] ORDER BY [EventDate]) AS TransitionRank
FROM StateChangeGrouping
)
SELECT [Item]
,MIN([EventDate]) AS [Start]
,MAX([EventDate]) AS [End]
,[GroupID]
FROM StateChangeRanked
WHERE GroupID > 0 AND TransitionRank = 1
GROUP BY [Item], GroupID
ORDER BY [Item], [Start]
以下は、SQL Server 2008 の実装です。
;WITH EventID
AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY [Item] ORDER BY [EventDate]) RowNr
,[Item]
,[EventDate]
,[Event]
FROM [dbo].[EventStates]
), StateChange
AS
(
SELECT C.RowNr
,P.RowNr AS P_RowNr
,C.[Item]
,C.[EventDate]
,C.[Event]
,P.[Event] AS P_Event
,( -- First determine if a state change to on has occurred.
CASE
WHEN C.[Event] = 'On' AND P.[Event] IS NULL THEN 1
WHEN C.[Event] = 'On' AND C.[Event] <> P.[Event] THEN 1
ELSE 0
END
) [StateChanged]
FROM EventID C
LEFT OUTER JOIN EventID P ON C.Item = P.Item AND C.RowNr = P.RowNr + 1
), StateChangeGrouping
AS
(
SELECT ST1.[Item]
,ST1.[EventDate]
,ST1.[Event]
,ST1.[StateChanged]
,(
SELECT SUM([StateChanged])
FROM StateChange ST2
WHERE ST2.Item = ST1.Item AND ST2.RowNr <= ST1.RowNr
) AS GroupID
FROM StateChange ST1
), StateChangeRanked
AS
(
SELECT [Item]
,[EventDate]
,[Event]
,[StateChanged]
,[GroupID]
,ROW_NUMBER() OVER (PARTITION BY [Item], GroupID, [Event] ORDER BY [EventDate]) AS TransitionRank
FROM StateChangeGrouping
)
SELECT [Item]
,MIN([EventDate]) AS [Start]
,MAX([EventDate]) AS [End]
,[GroupID]
FROM StateChangeRanked
WHERE GroupID > 0 AND TransitionRank = 1
GROUP BY [Item], GroupID
ORDER BY [Item], [Start]