2009-08-17 3 views
2

У меня есть таблица с 500 000 + записями. Каждая запись имеет поле LineNumber, которое не является уникальным, а не частью первичного ключа. Каждая запись имеет поле CreatedOn.Запрос T-SQL для повторения записей флага

Мне нужно обновить все 500 000 записей, чтобы идентифицировать повторяющиеся записи.

Повторные записи определяются записью, которая имеет тот же номер строки в течение последних семи дней своего поля CreateOn.

alt text http://i30.tinypic.com/27xq7oz.jpg

В диаграмме выше строке 4 является повторением, потому что это произошло только пять дней, так как строка 1. Строка 6 не повторится, даже если это происходит только четыре дня подряд 4, но сама строка 4 уже повторяется, поэтому строка 6 можно сравнить только с строкой 1, которая за девять дней до строки 6, поэтому строка 6 не является повторением.

Я не знаю, как обновить поле IsRepeat, пройдя каждую запись один за другим с помощью курсора или что-то в этом роде.

Я не верю, что курсоры - это путь, но я застрял в любом другом возможном решении.

Я подумал, может быть, общие выражения таблицы могут помочь, но у меня нет опыта с ними, и я понятия не имею, с чего начать.

В основном этот же процесс необходимо выполнять на столе каждый день, так как таблица усечена и повторно заполняется каждый день. Когда таблица будет повторно заполнена, я должен пройти и перемаркировать каждую запись, если она повторяется или нет.

Некоторая помощь была бы очень признательна.

UPDATE

Вот скрипт для создания таблицы и вставки данных испытаний

USE [Test] 
GO 

/****** Object: Table [dbo].[Job] Script Date: 08/18/2009 07:55:25 ******/ 
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Job]') AND type in (N'U')) 
DROP TABLE [dbo].[Job] 
GO 

USE [Test] 
GO 

/****** Object: Table [dbo].[Job] Script Date: 08/18/2009 07:55:25 ******/ 
SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Job]') AND type in (N'U')) 
BEGIN 
CREATE TABLE [dbo].[Job](
    [JobID] [int] IDENTITY(1,1) NOT NULL, 
    [LineNumber] [nvarchar](20) NULL, 
    [IsRepeat] [bit] NULL, 
    [CreatedOn] [smalldatetime] NOT NULL, 
CONSTRAINT [PK_Job] PRIMARY KEY CLUSTERED 
(
    [JobID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
END 
GO 


SET NOCOUNT ON 

INSERT INTO dbo.Job VALUES ('1006',NULL,'2009-07-01 07:52:08') 
INSERT INTO dbo.Job VALUES ('1019',NULL,'2009-07-01 08:30:01') 
INSERT INTO dbo.Job VALUES ('1028',NULL,'2009-07-01 09:30:35') 
INSERT INTO dbo.Job VALUES ('1005',NULL,'2009-07-01 10:51:10') 
INSERT INTO dbo.Job VALUES ('1005',NULL,'2009-07-02 09:22:30') 
INSERT INTO dbo.Job VALUES ('1027',NULL,'2009-07-02 10:27:28') 
INSERT INTO dbo.Job VALUES (NULL,NULL,'2009-07-02 11:15:33') 
INSERT INTO dbo.Job VALUES ('1029',NULL,'2009-07-02 13:01:13') 
INSERT INTO dbo.Job VALUES ('1014',NULL,'2009-07-03 12:05:56') 
INSERT INTO dbo.Job VALUES ('1029',NULL,'2009-07-03 13:57:34') 
INSERT INTO dbo.Job VALUES ('1025',NULL,'2009-07-03 15:38:54') 
INSERT INTO dbo.Job VALUES ('1006',NULL,'2009-07-04 16:32:20') 
INSERT INTO dbo.Job VALUES ('1025',NULL,'2009-07-05 13:46:46') 
INSERT INTO dbo.Job VALUES ('1029',NULL,'2009-07-05 15:08:35') 
INSERT INTO dbo.Job VALUES ('1000',NULL,'2009-07-05 15:19:50') 
INSERT INTO dbo.Job VALUES ('1011',NULL,'2009-07-05 16:37:19') 
INSERT INTO dbo.Job VALUES ('1019',NULL,'2009-07-05 17:14:09') 
INSERT INTO dbo.Job VALUES ('1009',NULL,'2009-07-05 20:55:08') 
INSERT INTO dbo.Job VALUES (NULL,NULL,'2009-07-06 08:29:29') 
INSERT INTO dbo.Job VALUES ('1002',NULL,'2009-07-07 11:22:38') 
INSERT INTO dbo.Job VALUES ('1029',NULL,'2009-07-07 12:25:23') 
INSERT INTO dbo.Job VALUES ('1023',NULL,'2009-07-08 09:32:07') 
INSERT INTO dbo.Job VALUES ('1005',NULL,'2009-07-08 09:46:33') 
INSERT INTO dbo.Job VALUES ('1016',NULL,'2009-07-08 10:09:08') 
INSERT INTO dbo.Job VALUES ('1023',NULL,'2009-07-09 10:45:04') 
INSERT INTO dbo.Job VALUES ('1027',NULL,'2009-07-09 11:31:23') 
INSERT INTO dbo.Job VALUES ('1005',NULL,'2009-07-09 13:10:06') 
INSERT INTO dbo.Job VALUES ('1006',NULL,'2009-07-09 15:04:06') 
INSERT INTO dbo.Job VALUES ('1010',NULL,'2009-07-09 17:32:16') 
INSERT INTO dbo.Job VALUES ('1012',NULL,'2009-07-09 19:51:28') 
INSERT INTO dbo.Job VALUES ('1000',NULL,'2009-07-10 15:09:42') 
INSERT INTO dbo.Job VALUES ('1025',NULL,'2009-07-10 16:15:31') 
INSERT INTO dbo.Job VALUES ('1006',NULL,'2009-07-10 21:55:43') 
INSERT INTO dbo.Job VALUES ('1005',NULL,'2009-07-11 08:49:03') 
INSERT INTO dbo.Job VALUES ('1022',NULL,'2009-07-11 16:47:21') 
INSERT INTO dbo.Job VALUES ('1026',NULL,'2009-07-11 18:23:16') 
INSERT INTO dbo.Job VALUES ('1010',NULL,'2009-07-11 19:49:31') 
INSERT INTO dbo.Job VALUES ('1029',NULL,'2009-07-12 11:57:26') 
INSERT INTO dbo.Job VALUES ('1003',NULL,'2009-07-13 08:32:20') 
INSERT INTO dbo.Job VALUES ('1005',NULL,'2009-07-13 09:31:32') 
INSERT INTO dbo.Job VALUES ('1021',NULL,'2009-07-14 09:52:54') 
INSERT INTO dbo.Job VALUES ('1021',NULL,'2009-07-14 11:22:31') 
INSERT INTO dbo.Job VALUES ('1023',NULL,'2009-07-14 11:54:14') 
INSERT INTO dbo.Job VALUES (NULL,NULL,'2009-07-14 15:17:08') 
INSERT INTO dbo.Job VALUES ('1005',NULL,'2009-07-15 13:27:08') 
INSERT INTO dbo.Job VALUES ('1010',NULL,'2009-07-15 14:10:56') 
INSERT INTO dbo.Job VALUES ('1011',NULL,'2009-07-15 15:20:50') 
INSERT INTO dbo.Job VALUES ('1028',NULL,'2009-07-15 15:39:18') 
INSERT INTO dbo.Job VALUES ('1012',NULL,'2009-07-15 16:06:17') 
INSERT INTO dbo.Job VALUES ('1017',NULL,'2009-07-16 11:52:08') 

SET NOCOUNT OFF 
GO 

ответ

1

Игнорирует LineNumber значение null. Как следует обрабатывать IsRepeat в этом случае?

Он работает для данных испытаний. Будет ли это достаточно эффективно для объемов производства?

В случае дублирования (LineNumber, CreatedOn) для пар произвольно выбирайте один.(Тот, с минимальным JobId)

Основная идея:

  1. Получить все пары JobId что по крайней мере семь дней друг от друга, по номер строки.
  2. Подсчитайте количество строк более семи дней с левой стороны, вверх и включая правую сторону. (НСТ)
  3. Тогда мы знаем, если JobId х не повтор, следующий не повторить это пару с X на с левой стороны, а также НКТ = 1
  4. Используйте рекурсивное ОТВ, чтобы начать с первого ряда для каждый LineNumber
  5. Рекурсивный элемент использует пару с подсчетами для получения следующей строки.
  6. Наконец-то обновите, установив все IsRepeat на 0 для не-повторов и 1 для всего остального.

; with AllPairsByLineNumberAtLeast7DaysApart (LineNumber 
      , LeftJobId 
      , RightJobId 
      , BeginCreatedOn 
      , EndCreatedOn) as 
     (select l.LineNumber 
      , l.JobId 
      , r.JobId 
      , dateadd(day, 7, l.CreatedOn) 
      , r.CreatedOn 
     from Job l 
     inner join Job r 
      on l.LineNumber = r.LineNumber 
      and dateadd(day, 7, l.CreatedOn) < r.CreatedOn 
      and l.JobId <> r.JobId) 
    -- Count the number of rows within from BeginCreatedOn 
    -- up to and including EndCreatedOn 
    -- In the case of CreatedOn = EndCreatedOn, 
    -- include only jobId <= jobid, to handle ties in CreatedOn   
    , AllPairsCount(LineNumber, LeftJobId, RightJobId, Cnt) as 
     (select ap.LineNumber, ap.LeftJobId, ap.RightJobId, count(*) 
     from AllPairsByLineNumberAtLeast7DaysApart ap 
     inner join Job j 
      on j.LineNumber = ap.LineNumber 
      and ap.BeginCreatedOn <= j.createdOn 
      and (j.CreatedOn < ap.EndCreatedOn 
       or (j.CreatedOn = ap.EndCreatedOn 
        and j.JobId <= ap.RightJobId)) 
     group by ap.LineNumber, ap.LeftJobId, ap.RightJobId) 
    , Step1 (LineNumber, JobId, CreatedOn, RN) as 
     (select LineNumber, JobId, CreatedOn 
      , row_number() over 
       (partition by LineNumber order by CreatedOn, JobId) 
     from Job) 
    , Results (JobId, LineNumber, CreatedOn) as  
     -- Start with the first rows. 
     (select JobId, LineNumber, CreatedOn 
     from Step1 
     where RN = 1 
     and LineNumber is not null 
     -- get the next row 
     union all 
     select j.JobId, j.LineNumber, j.CreatedOn 
     from Results r 
     inner join AllPairsCount apc on apc.LeftJobId = r.JobId 
     inner join Job j 
      on j.JobId = apc.RightJobId 
      and apc.CNT = 1) 
    update j 
    set IsRepeat = case when R.JobId is not null then 0 else 1 end 
    from Job j 
    left outer join Results r 
     on j.JobId = R.JobId 
    where j.LineNumber is not null 

EDIT:

После того как я выключил компьютер прошлой ночью я понял, что я сделал вещи более сложными, чем они должны быть. Более простой (и на тестовых данных, несколько более эффективна) запрос:

Основная идея:

  1. Сформирован PotentialStep (FromJobId, ToJobId) Эти пары, где, если FromJobId не является повторением, чем ToJobId также не является повторением. (Первая строка за LineNumber более , чем через семь дней после FromJobId)
  2. Используйте рекурсивный КТР, чтобы начать с первого JobId для каждого LineNumber, а затем шаг, с помощью PontentialSteps, к каждому Номера Повторяя JobId

; with PotentialSteps (FromJobId, ToJobId) as 
    (select FromJobId, ToJobId 
    from (select f.JobId as FromJobId 
      , t.JobId as ToJobId 
      , row_number() over 
       (partition by f.LineNumber order by t.CreatedOn, t.JobId) as RN 
     from Job f 
     inner join Job t 
      on f.LineNumber = t.LineNumber 
      and dateadd(day, 7, f.CreatedOn) < t.CreatedOn) t 
     where RN = 1) 
, NonRepeats (JobId) as 
    (select JobId 
    from (select JobId 
      , row_number() over 
       (partition by LineNumber order by CreatedOn, JobId) as RN 
     from Job) Start 
    where RN = 1 
    union all 
    select J.JobId 
    from NonRepeats NR 
    inner join PotentialSteps PS 
     on NR.JobId = PS.FromJobId 
    inner join Job J 
     on PS.ToJobId = J.JobId) 
update J 
set IsRepeat = case when NR.JobId is not null then 0 else 1 end 
from Job J 
left outer join NonRepeats NR 
on J.JobId = NR.JobId 
where J.LineNumber is not null 
+0

Ничего себе! Я действительно должен быть на вершине CTE! Примеры, подобные этому, действительно подталкивают мои синапсы, пока я им пользуюсь. С нетерпением ждем, чтобы указать на этот шаг.:) – BlackMael

+0

Он также производит интригующий план выполнения ... Для тех, кто достаточно грустен, чтобы быть заинтересованным. Черт, я думаю, мне грустно, так как я уже посмотрел. – BlackMael

+0

Он игнорирует LineNumber IS NULL, но все в порядке. Я оставил IsRepeat для NULL на всякий случай, когда мне было нужно позаботиться. По большей части мне кажется, что мне действительно нужно по умолчанию FALSE, если LineNumber IS NULL – BlackMael

-2

Я не горжусь этим, он делает много предположений (например, что CreatedOn только дата, и (LineNUmber, CreatedOn) является ключом. Может потребоваться некоторая настройка, которая работает только с данными теста.

Другими словами, я создал это больше для интеллектуального любопытства, а не потому, что я считаю, что это подлинное решение. Конечным выбором может быть обновление для установки IsRepeat в базовой таблице на основе существования в строках в V4. Заключительное примечание перед тем, как позволить людям увидеть зло - могли ли люди поместить тестовые данные в комментарии для наборов данных, для которых он не работает. Возможно, это можно превратить в реальное решение:

with V1 as (
select t1.LineNumber,t1.CreatedOn,t2.CreatedOn as PrevDate from 
T1 t1 inner join T1 t2 on t1.LineNumber = t2.LineNumber and t1.CreatedOn > t2.CreatedOn and DATEDIFF(DAY,t2.CreatedOn,t1.CreatedOn) < 7 
), V2 as (
select v1.LineNumber,v1.CreatedOn,V1.PrevDate from V1 
union all 
select v1.LineNumber,v1.CreatedOn,v2.PrevDate from v1 inner join v2 on V1.LineNumber = v2.LineNumber and v1.PrevDate = v2.CreatedOn 
), V3 as (
select LineNumber,CreatedOn,MIN(PrevDate) as PrevDate from V2 group by LineNumber,CreatedOn 
), V4 as (
select LineNumber,CreatedOn from V3 where DATEDIFF(DAY,PrevDate,CreatedOn) < 7 
) 
select 
    T1.LineNumber, 
    T1.CreatedOn, 
    CASE WHEN V4.LineNumber is Null then 0 else 1 end as IsRepeat 
from 
    T1 
     left join 
    V4 
     on 
      T1.LineNumber = V4.LineNumber and 
      T1.CreatedOn = V4.CreatedOn 
order by T1.CreatedOn,T1.LineNumber 
option (maxrecursion 7) 
+0

LineNumber не является частью первичного ключа CreatedOn имеет компонент времени и, по существу, smalldatetime я выложу некоторые данные для вас очень скоро я не знаю, как ваш запрос работает еще, но в очень ограниченном наборе данных, которые я приготовил, кажется, работает :) – BlackMael

+0

-1. Это возвращает все строки для LineNumber> семь дней от первого, поскольку это не повторение. Он не обрабатывает повторы после второй строки, не повторяющейся. Посмотрите на LineNumber 1005 в тестовых данных, отправленных OP. Создано только 2009-07-01 10:51:00 и 2009-07-09 13:10:00 должно быть IsRepeate = False. У вас есть все строки> = 2009-07-08 09:47:00 (Конечно, 07-08 происходит от целых дней, но вы не должны получать каждую дату с тех пор, как не повторять, если я не понял OP.) –

+0

Это было опубликовано до обновления BlackMaels, когда единственными тестовыми данными были 6 строк таблицы в верхней части сообщения. На основе этих данных он вернул правильный набор результатов. –

-1
UPDATE Jobs 
SET Jobs.IsRepeat = 0 -- mark all of them IsRepeat = false 

UPDATE Jobs 
SET Jobs.IsRepeat = 1 
WHERE EXISTS 
    (SELECT TOP 1 i.LineNumber FROM Jobs i WHERE i.LineNumber = Jobs.LineNumber 
    AND i.CreatedOn <> Jobs.CreatedOn and i.CreatedOn BETWEEN Jobs.CreatedOn - 7 
    AND Jobs.CreatedOn) 

ПРИМЕЧАНИЕ: Я надеюсь, что это вам поможет. Дайте мне знать, если вы обнаружите какое-либо несоответствие, которое вы встретите в большом наборе данных.

+0

Извините, это не учитывает, что Job не является повторением, если единственное другое задание с тем же номером строки внутри 7 дней - это повторение. – BlackMael

+0

@BlackMael: Не могли бы вы показать, что с примером пожалуйста? – shahkalpesh

Смежные вопросы