2009-08-17 9 views
2

J'ai une table qui aura plus de 500 000 enregistrements. Chaque enregistrement a un champ LineNumber qui n'est pas unique et ne fait pas partie de la clé primaire. Chaque enregistrement a un champ CreatedOn.Requête T-SQL pour marquer les enregistrements répétés

J'ai besoin de mettre à jour tous les 500 000 enregistrements pour identifier les enregistrements répétés.

Un enregistrement de répétition est défini par un enregistrement qui a le même numéro de ligne au cours des sept derniers jours de son champ CreatedOn.

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

Dans le schéma ci-dessus la ligne 4 est une répétition parce qu'il est survenu cinq jours depuis la ligne 1. La ligne 6 est pas une répétition, même si elle se produit seulement quatre jours depuis la ligne 4, mais la ligne 4 lui-même est déjà une répétition, donc la rangée 6 ne peut être comparée à la rangée 1 qui est neuf jours avant la rangée 6, donc la rangée 6 n'est pas une répétition.

Je ne sais pas comment mettre à jour le champ IsRepeat en parcourant chaque enregistrement un par un via un curseur ou quelque chose.

Je ne crois pas que les curseurs soient la solution, mais je suis coincé avec toute autre solution possible. J'ai pensé que peut-être des expressions de table communes peuvent être utiles, mais je n'ai aucune expérience avec eux et je n'ai aucune idée par où commencer.

Fondamentalement, ce même processus doit être effectué sur la table tous les jours car la table est tronquée et ranimée tous les jours. Une fois la table remplie, je dois passer en revue et re-marquer chaque enregistrement s'il s'agit d'une répétition ou non.

De l'aide serait grandement appréciée.

MISE À JOUR

Voici un script pour créer une table et insérer des données de test

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 

Répondre

1

Ignore LineNumber est null. Comment IsRepeat devrait-il être traité dans ce cas?

Fonctionne pour les données de test. Si ce sera assez efficace pour les volumes de production?

Dans le cas de doublons (LineNumber, CreatedOn) sur les paires, choisissez-en un arbitrairement.(Celui avec JobId minimum)

idée de base:

  1. Obtenez toutes les paires JOBID que sont au moins sept jours d'intervalle, par numéro de ligne.
  2. Comptez le nombre de lignes qui sont plus de sept jours à partir du côté gauche, jusqu'à , y compris le côté droit. (CNT)
  3. Ensuite, nous savons si JobId x est pas une répétition, le prochain pas une répétition est la paire avec X sur le côté gauche et CNT = 1
  4. Utilisez CTE récursive pour commencer la première ligne pour each LineNumber
  5. L'élément récursif utilise la paire avec des comptes pour obtenir la ligne suivante.
  6. Enfin mettre à jour, définissant tous IsRepeat à 0 pour les non-répétitions et 1 pour tout le reste.

; 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:

Après avoir éteint l'ordinateur hier soir, j'ai réalisé que je l'avais rendu les choses plus compliquées qu'elles devaient être. Un plus simple (et sur les données de test, un peu plus effecient) requête:

Idée de base:

  1. PotentialStep (FromJobId, ToJobId) Generated Ce sont les paires où se FromJobId n'est pas une répétition, que ToJobId n'est pas non plus une répétition. (Première rangée par LineNumber plus de sept jours à partir de FromJobId)
  2. Utiliser un CTE récursive pour démarrer à partir de la première JobId pour chaque LineNumber et alors l'étape, utilisant PontentialSteps, à chaque non Répétition 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

Wow! Je dois vraiment être au top des CTE! Des exemples comme celui-ci poussent vraiment mes synapses pendant que je m'habitue à eux. Au plaisir de pointer celui-ci à travers ses allures ..:) – BlackMael

+0

Il produit également un plan d'exécution intrigant ... Pour ceux assez triste pour être intéressé. Merde, je suppose que je suis assez triste depuis que j'ai regardé déjà .. – BlackMael

+0

Il ignore LineNumber IS NULL, mais c'est d'accord. J'ai quitté IsRepeat pour NULLs juste au cas où je devais m'en soucier. La plupart du temps, je pense que je n'ai vraiment besoin de par défaut à FALSE si LineNumber IS NULL – BlackMael

-2

Je ne suis pas fier de cela, il fait beaucoup d'hypothèses (par exemple, que CreatedOn est seule date, et (LineNUmber, CreatedOn) est une clé.Un réglage peut être nécessaire, ne fonctionne qu'avec des données de test

En d'autres termes, je l'ai créé plus pour la curiosité intellectuelle plutôt que parce que je pense que c'est une véritable solution. La sélection finale peut être une mise à jour pour définir IsRepeat dans la table de base, basée sur l'existence de lignes dans V4. Note finale avant de laisser les gens voir le mal - les gens pourraient-ils publier des données de test dans les commentaires pour les ensembles de données pour lesquels cela ne fonctionne pas? Il pourrait être possible de transformer ceci en une vraie solution:

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 ne fait pas partie de la clé primaire CreatedOn a une composante de temps et est essentiellement SmallDateTime je posterai quelques données pour vous très peu de temps Je ne sais pas comment fonctionne votre requête encore, mais sur un ensemble très limité des données que j'avais cuites il semble fonctionner :) – BlackMael

+0

-1. Cela retourne toutes les lignes pour un numéro de ligne> sept jours à partir du premier comme étant une non-répétition. Il ne gère pas les répétitions après la deuxième ligne non répétée. Regardez LineNumber 1005 dans les données de test publiées par OP. Seulement créé le 2009-07-01 10:51:00 et 2009-07-09 13:10:00 devrait être IsRepeate = False. Vous avez toutes les lignes> = 2009-07-08 09:47:00 (Bien sûr, le 07-08 vient de faire des journées entières, mais vous ne devriez pas obtenir toutes les dates depuis comme une non-répétition, sauf si j'ai mal compris OP.) –

+0

Cela a été publié avant la mise à jour de BlackMaels, lorsque les seules données de test étaient les 6 lignes en haut de la publication. Sur la base de ces données, il a renvoyé le jeu de résultats correct. –

-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) 

NOTE: J'espère que cela vous aide un peu. Faites-moi savoir, si vous trouvez une divergence que vous rencontrerez sur un plus grand ensemble de données.

+0

Désolé, cela ne tient pas compte du fait qu'un Job n'est pas une répétition si le seul autre travail avec le même numéro de ligne 7 jours est une répétition. – BlackMael

+0

@BlackMael: Pourriez-vous montrer cela avec un exemple s'il vous plaît? – shahkalpesh

Questions connexes