在不使用游标的情况下填写行中丢失的数据

时间:2018-09-25 10:24:18

标签: sql sql-server sql-server-2014

我有一个这样的表格(简化后):

CREATE TABLE [dbo].[test](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Extension] [varchar](30) NULL DEFAULT (''),
    [StartTimestamp] [datetime] NULL,
    [UserId] [varchar](30) NULL DEFAULT (''),
 CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

请考虑以下数据:

Id | Extension | StartTimestamp      | UserId
------------------------------------------------
1  |  100      | 2018-09-24 16:00:00 | A 
2  |  101      | 2018-09-24 16:01:15 | B
3  |  100      | 2018-09-24 16:01:14 | 
4  |  102      | 2018-09-24 16:02:24 | C
5  |  100      | 2018-09-24 16:05:00 | A
6  |  101      | 2018-09-24 16:07:00 | B 
7  |  101      | 2018-09-24 16:30:00 |
8  |  100      | 2018-09-24 17:00:00 | D

现在,我要按照以下规则填充UserId列的空隙: 一定扩展名的记录,且不超过10分钟 因此,对于ID为3的行,它表示UserId A(因为它将找到ID为1的记录)。对于第7行,UserId将保持为空,因为该扩展名无法在10分钟内找到记录。 目前,我正在使用此查询来识别具有空格(与游标结合)的扩展名:

SELECT 
  DISTINCT b.Extension
FROM test b
 INNER JOIN (
  SELECT DISTINCT Extension
   FROM test
   WHERE
    UserId = ''
   ) a ON a.Extension=b.Extension
 WHERE
  b.UserId <> ''
 ORDER BY b.Extension

是否可以不使用游标(即使用单个更新语句)来实现这一目标?

3 个答案:

答案 0 :(得分:2)

我认为您可以只使用lag()

select t.*,
       (case when userid is not null then userid
             when (lag(StartTimestamp) over (partition by Extension 
 order by StartTimestamp) > 
                   dateadd(minute, -10, StartTimestamp)
                  )
             then lag(userid) over (partition by Extension 
 order by StartTimestamp)
        end) as imputed_userid
from test t;

答案 1 :(得分:1)

with Changes as (SELECT t1.id FROM test t1 
                        CROSS APPLY 
                                    (SELECT TOP 1 t2.UserId from test  t2 where
                                                             t2.Extension = t1.extension 
                                                            and  t1.id != t2.id 
                                                            and t2.starttimestamp 
                                                              between dateadd(minute,-10,t1.starttimestamp) 
                                                              AND t2.starttimestamp 
                                                            ORDER BY t2.starttimestamp DESC) DQ
                        WHERE COALESCE(t1.userid,'') = '' 
                )
                Update Test SET UserId = 
                        (SELECT UserId from changes WHERE Changes.Id = test.Id 
                            AND EXISTS(SELECT 0 FROm Changes c2 WHERE c2.Id = test.Id)
                        )

答案 2 :(得分:1)

我已经使用ROW_NUMBER()实现了此目的:

DECLARE @temp TABLE (
    [Id] [bigint] IDENTITY(1,1) NOT NULL PRIMARY KEY,
    [Extension] [varchar](30) NULL DEFAULT (''),
    [StartTimestamp] [datetime] NULL,
    [UserId] [varchar](30) NULL DEFAULT ('')
)

INSERT INTO @temp (Extension, StartTimestamp, UserId)
SELECT 100, {ts'2018-09-24 16:00:00.000'}, 'A' UNION ALL
SELECT 101, {ts'2018-09-24 16:01:15.000'}, 'B' UNION ALL
SELECT 100, {ts'2018-09-24 16:01:14.000'}, ''  UNION ALL
SELECT 102, {ts'2018-09-24 16:02:24.000'}, 'C' UNION ALL
SELECT 100, {ts'2018-09-24 16:05:00.000'}, 'A' UNION ALL
SELECT 101, {ts'2018-09-24 16:07:00.000'}, 'B' UNION ALL
SELECT 101, {ts'2018-09-24 16:30:00.000'}, ''

SELECT
    a.Id, a.Extension, a.StartTimestamp, a.UserId, b.StartTimestamp, b.UserId
FROM @temp a
JOIN (
    SELECT
        ROW_NUMBER() OVER(PARTITION BY a.Extension ORDER BY b.StartTimestamp) RowNo,
        a.Id, a.Extension, b.StartTimestamp, b.UserId
    FROM @temp a
    JOIN @temp b ON b.Extension = a.Extension -- edited to add this
    AND LEN(b.UserId) > 0
    AND b.StartTimestamp >= DATEADD(mi, -10, a.StartTimestamp)
    AND b.StartTimestamp < DATEADD(mi, 10, a.StartTimestamp)
    WHERE a.UserId = ''
) b ON b.Id = a.Id AND b.RowNo = 1