T-SQL连接以获取已配对和非配对的开始和停止记录

Joe*_*ton 5 sql sql-server-2008-r2

我有一个设计不佳的表,我没有设计,无法修复/更改,因为第三方黑莓应用程序写入它.关键是有一个开始记录和一个停止记录,用于跟踪没有连接的事件或验证是否匹配.黑莓应用程序无需将这些记录绑定在一起.我试图在其自身上创建一个连接,并使用begin创建临时表,并将一个结尾创建为全外连接它们.问题是我有重复的条目,条目应标记为没有配偶.现有数据的行和开始和结束记录都没有配对.我搜索了SO的答案,我发现了一些让我这么远的答案.我知道它很长,很抱歉.

有一个单一的令人惊讶地具有主键表.没有枢轴/交叉表.结构是

ID (int PK)
activityType varchar
beginEnd varchar ('begin' or 'end')
businessKey varchar nullable
date DATETIME
technician varchar
Run Code Online (Sandbox Code Playgroud)

表中还包含以下列,但这些列可以为空,对查询并不重要.

dateSubmitted DATETIME
gpsLatitude float
gpsLongitude float
note varchar
odometer int
Run Code Online (Sandbox Code Playgroud)

我现在的查询仍然存在欺骗:显示和排序ID和EndID仅用于调试

DECLARE @DateFrom DATETIME
DECLARE @DateTo DATETIME
SET @DateFrom='20101101'
SET @DateTo='20101102'
DECLARE @Incomplete VARCHAR(15)
SET @Incomplete = 'Incomplete'

DECLARE @StartEvents TABLE
(
[id] [numeric](19, 0) NOT NULL,
[activityType] [varchar](255) NOT NULL,
[beginEnd] [varchar](255) NULL,
[businessKey] [varchar](255) NULL,
[date] [datetime] NOT NULL,
[dateSubmitted] [datetime] NULL,
[gpsLatitude] [float] NULL,
[gpsLongitude] [float] NULL,
[note] [varchar](255) NULL,
[odometer] [int] NULL,
[technician] [varchar](255) NOT NULL
)

INSERT     @StartEvents
       ([ID],[activityType],[beginEnd],[businessKey],[date],[dateSubmitted],[gpsLatitude]
            ,[gpsLongitude],[note],[odometer],[technician])
SELECT      *
FROM        dbo.TimeEntry
WHERE 
[date] between @DateFrom AND @DateTo
AND beginEnd = 'Begin'
--AND [technician] = 'FRED'
ORDER by technician
------------------------------------------------------------
DECLARE @EndEvents TABLE
(
[id] [numeric](19, 0) NOT NULL,
[activityType] [varchar](255) NOT NULL,
[beginEnd] [varchar](255) NULL,
[businessKey] [varchar](255) NULL,
[date] [datetime] NOT NULL,
[dateSubmitted] [datetime] NULL,
[gpsLatitude] [float] NULL,
[gpsLongitude] [float] NULL,
[note] [varchar](255) NULL,
[odometer] [int] NULL,
[technician] [varchar](255) NOT NULL
)

INSERT     @EndEvents
       ([ID],[activityType],[beginEnd],[businessKey],[date],[dateSubmitted],[gpsLatitude]
            ,[gpsLongitude],[note],[odometer],[technician])
SELECT      *
FROM        dbo.TimeEntry
WHERE 
[date] between @DateFrom AND @DateTo AND
beginEnd = 'End'
--AND [technician] = 'FRED'
ORDER by technician

-- And then a conventional SELECT
SELECT     
StartEvents.id 
,EndEvents.id AS EndID
,COALESCE(
    StartEvents.activityType ,EndEvents.activityType ,'Not Available'
    ) AS ActivityType
--,StartEvents.beginEnd as [Begin] 
--,EndEvents.beginEnd AS [End]
,COALESCE (
    convert(VARCHAR(12), StartEvents.[date], 103), 
    convert(VARCHAR(12), EndEvents.[date], 103), @Incomplete
    ) as [Event Date] 
,COALESCE (
    convert(VARCHAR(12), EndEvents.[date], 103), @Incomplete 
    ) as [End Date] 
,COALESCE(
    CONVERT(VARCHAR(5) , StartEvents.dateSubmitted , 108) , @Incomplete
        ) AS StartTime
,COALESCE(
    CONVERT(VARCHAR(5) , EndEvents.dateSubmitted , 108) , @Incomplete
        ) AS EndTime
,COALESCE(
    StartEvents.note, EndEvents.note, ''
    ) as [Note]
,COALESCE(
    StartEvents.technician,EndEvents.technician,'Not Available'
    ) AS Technician 

FROM         
@StartEvents As StartEvents 

FULL OUTER JOIN

@EndEvents AS EndEvents ON 
StartEvents.technician = EndEvents.technician AND 
StartEvents.businessKey = EndEvents.businessKey AND 
StartEvents.activityType = EndEvents.activityType 
AND convert(VARCHAR(12), StartEvents.[date], 103) = convert(VARCHAR(12), EndEvents.[date], 103) 

-- WHERE 
    --StartEvents.[date] between @DateFrom AND @DateTo OR 
    --StartEvents.[dateSubmitted] between @DateFrom AND @DateTo 
ORDER BY 
    StartEvents.Technician,
    ID,ENDID
Run Code Online (Sandbox Code Playgroud)

数据:

id,activityType,beginEnd,businessKey,date,dateSubmitted,gpsLatitude,gpsLongitude,note,odometer,technician
23569,Standby,Begin,,2010-11-01 08:00:13.000,2010-11-01 08:26:45.533,34.139,-77.895,#1140,28766,barthur@fubar.com
23570,Travel,Begin,00100228002,2010-11-01 07:00:44.000,2010-11-01 08:34:15.370,35.0634,-80.7668,,18706,creneau@fubar.com
23571,Standby,End,,2010-11-01 08:30:08.000,2010-11-01 08:35:20.463,34.0918,-77.9002,#1140,28766,barthur@fubar.com
23572,Travel,Begin,00100226488,2010-11-01 08:30:41.000,2010-11-01 08:36:56.420,34.0918,-77.9002,,28766,barthur@fubar.com
23573,Travel,End,00100226488,2010-11-01 08:45:00.000,2010-11-01 08:44:15.553,34.0918,-77.9002,,28768,barthur@fubar.com
23574,OnSite,Begin,00100226488,2010-11-01 08:45:41.000,2010-11-01 09:24:23.943,34.0918,-77.9002,,0,barthur@fubar.com
23575,OnSite,End,00100226488,2010-11-01 09:30:10.000,2010-11-01 09:33:19.953,34.0918,-77.9002,,28768,barthur@fubar.com
23576,Travel,Begin,00100228137,2010-11-01 09:30:20.000,2010-11-01 09:34:57.330,34.0918,-77.9002,,28768,barthur@fubar.com
23577,Travel,End,00100228137,2010-11-01 09:45:51.000,2010-11-01 09:42:39.230,34.0918,-77.9002,,28771,barthur@fubar.com
23578,Travel,Begin,00100228138,2010-11-01 09:00:23.000,2010-11-01 09:58:22.857,34.9827,-80.5365,,18749,creneau@fubar.com
23579,OnSite,Begin,00100228137,2010-11-01 09:45:47.000,2010-11-01 10:41:10.563,34.139,-77.895,,0,barthur@fubar.com
23580,OnSite,End,00100228137,2010-11-01 10:45:43.000,2010-11-01 11:09:14.393,34.139,-77.895,,28771,barthur@fubar.com
23581,OnSite,Begin,00100228142,2010-11-01 10:45:42.000,2010-11-01 11:29:26.447,34.139,-77.895,#1015,28771,barthur@fubar.com
23582,OnSite,End,00100228142,2010-11-01 11:15:18.000,2010-11-01 11:55:28.603,34.139,-77.895,#1015,28771,barthur@fubar.com
23583,Travel,Begin,,2010-11-01 11:15:06.000,2010-11-01 11:56:01.633,34.139,-77.895,"#1142 Fuel, #1154 Tickets",28771,barthur@fubar.com
23584,Travel,End,,2010-11-01 12:00:47.000,2010-11-01 12:07:54.867,34.139,-77.895,"#1154, #1142",28774,barthur@fubar.com
23585,Travel,End,,2010-11-01 12:00:47.000,2010-11-01 12:07:55.087,34.139,-77.895,"#1154, #1142",28774,barthur@fubar.com
23586,Break,Begin,,2010-11-01 12:00:26.000,2010-11-01 12:08:06.007,34.139,-77.895,#1153,28774,barthur@fubar.com
23587,Travel,End,,2010-11-01 12:00:47.000,2010-11-01 12:08:06.040,34.139,-77.895,"#1154, #1142",28774,barthur@fubar.com
23588,Break,Begin,,2010-11-01 12:00:26.000,2010-11-01 12:08:06.070,34.139,-77.895,#1153,28774,barthur@fubar.com
23589,Travel,End,,2010-11-01 12:00:47.000,2010-11-01 12:16:02.673,34.139,-77.895,"#1154, #1142",28774,barthur@fubar.com
23590,Travel,End,,2010-11-01 12:00:47.000,2010-11-01 12:16:14.220,34.139,-77.895,"#1154, #1142",28774,barthur@fubar.com
23591,Travel,Begin,00100228000,2010-11-01 11:45:19.000,2010-11-01 12:35:46.363,35.0634,-80.7668,,18760,creneau@fubar.com
23592,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:58:51.050,34.0918,-77.9002,,28774,barthur@fubar.com
23593,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:03.830,34.0918,-77.9002,,28774,barthur@fubar.com
23594,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:03.893,34.1594,-77.8929,,28774,barthur@fubar.com
23595,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:03.940,34.1594,-77.8929,,28774,barthur@fubar.com
23596,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:15.880,34.1594,-77.8929,,28774,barthur@fubar.com
23597,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:15.927,34.2743,-77.8668,,28774,barthur@fubar.com
23598,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:15.987,34.2743,-77.8668,,28774,barthur@fubar.com
23599,Travel,Begin,00100228166,2010-11-01 14:00:13.000,2010-11-01 14:29:45.320,35.0634,-80.7668,,18779,creneau@fubar.com
23600,Travel,End,00100227980,2010-11-01 15:15:58.000,2010-11-01 15:15:40.403,35.3414,-78.0325,,28880,barthur@fubar.com
23601,Travel,Begin,00100228205,2010-11-01 15:30:46.000,2010-11-01 15:41:41.810,35.0661,-80.8376,,18781,creneau@fubar.com
23602,OnSite,Begin,00100227980,2010-11-01 15:15:23.000,2010-11-01 15:59:45.203,35.3873,-77.9395,,28880,barthur@fubar.com
23603,OnSite,End,00100227980,2010-11-01 16:15:22.000,2010-11-01 16:06:09.150,35.3873,-77.9395,,28880,barthur@fubar.com
23604,Travel,Begin,00100228007,2010-11-01 16:15:15.000,2010-11-01 16:15:25.253,35.3873,-77.9395,,28880,barthur@fubar.com
23605,Travel,Begin,,2010-11-01 16:15:12.000,2010-11-01 16:20:49.933,35.0445,-80.8227,Return trip home,18785,creneau@fubar.com
23606,Travel,End,00100228007,2010-11-01 16:30:48.000,2010-11-01 16:26:43.360,35.3873,-77.9395,,28884,barthur@fubar.com
23607,Travel,End,,2010-11-01 17:30:14.000,2010-11-01 17:23:57.897,35.2724,-81.1577,Return trip home,18822,creneau@fubar.com
23608,OnSite,Begin,00100228007,2010-11-01 16:30:48.000,2010-11-01 18:38:32.700,35.3941,-77.994,,28880,barthur@fubar.com
23609,Travel,Begin,00100228209,2010-11-01 17:45:16.000,2010-11-01 18:39:05.683,35.3941,-77.994,,28884,barthur@fubar.com
23610,OnSite,End,00100228007,2010-11-01 17:45:52.000,2010-11-01 18:41:36.980,35.3941,-77.994,,28884,barthur@fubar.com
23611,OnSite,Begin,00100228209,2010-11-01 18:00:38.000,2010-11-01 18:42:12.763,35.3941,-77.994,,28888,barthur@fubar.com
23612,OnSite,End,00100228209,2010-11-01 18:30:44.000,2010-11-01 18:43:29.123,35.3941,-77.994,,28888,barthur@fubar.com
23613,Standby,Begin,,2010-11-01 18:30:58.000,2010-11-01 18:45:28.857,35.3941,-77.994,#1157 ergo,28888,barthur@fubar.com
23614,Standby,End,,2010-11-01 18:45:26.000,2010-11-01 18:46:01.167,35.3941,-77.994,#1157 ergo redo,28888,barthur@fubar.com
23615,Travel,Begin,,2010-11-01 18:45:24.000,2010-11-01 18:47:37.803,35.3941,-77.994,RTN,28888,barthur@fubar.com
23616,Travel,End,,2010-11-01 20:45:05.000,2010-11-01 20:34:39.433,34.139,-77.895,#1142 Fueled,28990,barthur@fubar.com
Run Code Online (Sandbox Code Playgroud)

在此图像中,您会看到突出显示的行显示6个结束时间,具有相同的开始时间.记录14和15显示2开始,没有结束.
在此输入图像描述

Tim*_*ner 2

这是一个滥用 row_number 函数的方法。请检查评论以获取一些解释。

;with Seq as (
    -- Create a master sequence of events
    -- Trust the date column to be accurate (don't match a Begin to an earlier End)
    select id, activitytype, beginend
        , coalesce(businesskey, '') as businesskey -- Needed to match nulls as equal
        , [date], technician, note
        , row_number() over (partition by technician, businesskey, activitytype order by [date], beginend, id) as rownumber
    from TimeEntry
)
select b.id as BeginID
    , e.id as EndID
    , coalesce(b.technician, e.technician) as Technician
    , coalesce(b.businesskey, e.businesskey) as BusinessKey
    , coalesce(b.activitytype, e.activitytype) as ActivityType
    , coalesce(convert(char(10), b.[date], 103), 'Incomplete') as BeginDate
    , coalesce(convert(char(10), e.[date], 103), 'Incomplete') as EndDate
    , coalesce(convert(char(5), b.[date], 108), 'Incomplete') as BeginTime
    , coalesce(convert(char(5), e.[date], 108), 'Incomplete') as EndTime
    , b.note as BeginNote
    , e.note as EndNote
from (select * from Seq where beginend = 'Begin') b -- Get all Begins
    full outer join (select * from Seq where beginend = 'End') e -- Get all Ends
        on b.technician = e.technician
            and b.businesskey = e.businesskey
            and b.activitytype = e.activitytype
            and b.rownumber = e.rownumber - 1 -- Match a Begin with only the very next End of that type
order by coalesce(b.[date], e.[date])
    , coalesce(b.id, e.id)
    , coalesce(b.technician, e.technician)
    , coalesce(b.businesskey, e.businesskey)
    , coalesce(b.activitytype, e.activitytype)
Run Code Online (Sandbox Code Playgroud)

SQL Fiddle应该任何人都想要 DDL 或演示。