2017-05-04 3 views
0

Ich habe eine BeispieltabelleAbfrage gleichen Ereigniscode mit mindestens einem Stundenintervall auszuwählen

CREATE TABLE [dbo].[wt](
[id] [int] NULL, 
[dt] [datetime] NULL, 
[txt] [nvarchar](50) NULL 
) ON [PRIMARY] 

GO 

INSERT INTO [dbo].[wt] 
     ([id] 
     ,[dt] 
     ,[txt]) 
VALUES 
(1, '2017-01-01 00:01:00.000', 't1'), 
(2, '2017-01-01 00:03:00.000', 't1'), 
(3, '2017-01-01 00:02:00.000', 't1'), 
(4, '2017-01-01 01:04:00.000', 't1'), 
(5, '2017-01-01 02:10:00.000', 't1'), 
(6, '2017-01-01 00:01:00.000', 't1'), 
(7, '2017-01-01 01:05:00.000', 't1'), 
(8, '2017-01-01 02:10:00.000', 't2'), 
(9, '2017-01-01 00:03:00.000', 't2'), 
(10,'2017-01-01 01:04:00.000', 't2'), 
(11,'2017-01-01 00:52:00.000', 't1') 

Ich möchte eine Liste der txt-Code und dt Veröffentlichung txt Code gruppiert haben, wo Intervall beetwen txt Auftreten mindestens eine Stunde und nichts dazwischen. Um zu klären, wann t1 das erste Mal um '2017-01-01 00: 01: 00.000' auftritt, dann ist das nächste Vorkommen, das ich suche, nach mindestens einer Stunde , die '2017-01-01 01: 04: 00.000' sein wird dritte Vorkommen, die ich suche, ist nach mindestens einer Stunde von '2017-01-01 01: 04: 00.000' und so weiter.

Nach einiger Suche fand ich so etwas wie diese

;with a as (
select txt, dt, 
rn = row_number() over (partition by txt order by dt asc) 
from [wt]), 
b as (
select txt, dt, dt as dt2, rn, null tm, 0 recurrence 
from a 
where rn = 1 
union all 
select a.txt, a.dt, a.dt, 
a.rn, datediff(MINUTE,a.dt,b.dt) tm, 
case when dateadd(MINUTE,-60,a.dt) < b.dt then recurrence + 1 else 0 end 
from b join a 
on b.rn = a.rn - 1 and b.txt = a.txt 
) 
select txt, dt, rn, tm, recurrence 
from b 
where recurrence = 0 
order by txt, dt 

aber das war nicht gut, weil das Intervall nicht vom ersten Auftreten gezählt wird aber von den letzten, so habe ich

txt dt rn tm recurrence 
t1 2017-01-01 00:01:00.000 1 NULL 0 
t1 2017-01-01 02:10:00.000 8 -65 0 
t2 2017-01-01 00:03:00.000 1 NULL 0 
t2 2017-01-01 01:04:00.000 2 -61 0 
t2 2017-01-01 02:10:00.000 3 -66 0 

I Ich denke, ich habe einen Workaround gefunden, da ich in diesem Fall innerhalb der gleichen Stunde Aufnahmen machen konnte, aber ich bin nicht zufrieden mit dieser Lösung.

Alle Vorschläge zur Verbesserung des Skripts, damit das Intervall in Minuten eingegeben werden kann, wäre willkommen.

+1

Was ist Ihre erwartete Ausgabe? –

Antwort

0

I der Art wie ein Verfahren, das eine Blasensortierung ist. Das Problem, das ich bei rekursiven Operationen gefunden habe, ist, dass sie gut für kleine Sätze funktionieren (denke weniger als 5 oder 10k), dann benimm dich schrecklich, wenn du größer wirst. Aus diesem Grund mag ich einen Cursoransatz, bei dem Sie im Wesentlichen sagen: "Sind Sie größer als ein Kriterium? Ja, nein. Einfügen oder Ignorieren, Löschen, Weitergehen." Auf diese Weise bewerten Sie jedes Element einmal und nur einmal, nicht jede Variation eines Themas der Rekursion.

DECLARE @Temp TABLE 
    (
    id INT 
    , dt DATETIME 
    , txt VARCHAR(8) 
    , rwn INT 
) 

DECLARE @Holder TABLE 
    (
    id INT 
    , dt DATETIME 
    , txt VARCHAR(8) 
    , Dif int 
) 

INSERT INTO @Temp 
SELECT *, row_number() over (partition by txt order by dt, id) AS rn 
From wt 

WHILE EXISTS (SELECT 1 FROM @Temp) 
BEGIN 
    DECLARE 
     @CurId INT 
    , @CurDt DATETIME 
    , @Curtxt VARCHAR(8) 
    , @LastDate DATETIME 
    ; 

    SELECT TOP 1 @CurId = Id, @CurDt = Dt, @Curtxt = txt FROM @Temp ORDER BY txt, rwn 

    --If there is not entry you need a single entry 
    IF NOT EXISTS (SELECT TOP 1 * FROM @Holder) 
     BEGIN 
      INSERT INTO @Holder VALUES (@CurId, @CurDt, @curtxt, null) 
     END 
    ELSE 
     --if you reset the grouping you need to reset and begin anew 
     IF (SELECT rwn FROM @Temp WHERE Id = @CurId) = 1 
     BEGIN 
      INSERT INTO @Holder VALUES (@CurId, @CurDt, @curtxt, null) 
     END 
     --if you are going along check the logic for the difference of what the last was compared to the current 
     ELSE 
     BEGIN 
     SELECT TOP 1 @LastDate = dt FROM @Holder ORDER BY id desc 

     IF DATEDIFF(HOUR, @LastDate, @CurDt) >= 1 
     BEGIN 
      INSERT INTO @Holder VALUES (@CurId, @CurDt, @curtxt, DATEDIFF(MINUTE, @LastDate, @CurDt)) 
     END 
     END 

    --Delete the running values and loop again 
    DELETE @Temp WHERE Id = @CurId 
END 

Select * 
From @Holder 
+0

Danke djangojazz! Obwohl ich nach einem anderen Ansatz als dem Kursor suchte, funktioniert es perfekt. – andrewp

+0

Kein Problem, Sie haben Recht, da SQL im Allgemeinen besser als ein ergebnissatzbasierter Ansatz und nicht als Iteration ist. Allerdings müssen Sie die Kosten der Maschine abwägen und wenn Sie etwas haben, das einige N Factoral Zeiten wiederholt auswerten muss, ist das auf lange Sicht schlecht. Wenn Sie ohne zu zögern sagen könnten: "Mein Set wird niemals über 5k wachsen", dann ist die Rekursion vielleicht besser. – djangojazz

1

Wenn ich richtig verstanden habe, denke ich, das Folgende tut, was Sie brauchen.

CREATE TABLE #T (id INT , rn INT, txt VARCHAR(10), dt DATETIME, lagDiff INT, runningDiff INT) 

INSERT INTO #T (id, rn, txt, dt, lagDiff, runningDiff) 
SELECT id 
     , ROW_NUMBER() OVER(PARTITION BY txt ORDER BY dt, id) -1 rn 
     , txt 
     , dt 
     , DATEDIFF(MINUTE, COALESCE(LAG(dt) OVER(PARTITION BY txt ORDER BY dt, id), dt), dt) Diff 
     , DATEDIFF(MINUTE, COALESCE(FIRST_VALUE(dt) OVER(PARTITION BY txt ORDER BY dt, id), dt), dt) RunningDiff 
FROM wt 

; WITH CTE AS (
    SELECT *, 1 AS Level 
    FROM #T 
    WHERE rn = 0 
    UNION ALL 
    SELECT T.*, CTE.Level + 1 
    FROM #T T 
      INNER JOIN CTE ON CTE.txt = T.txt AND CTE.rn < T.rn AND T.runningDiff - 60 > CTE.runningDiff 
    WHERE T.rn > 0 
) 
, X AS (
    SELECT txt 
     , Level 
     , MIN(rn) rn 
    FROM CTE 
    GROUP BY txt, Level 
) 
SELECT #T.* 
FROM X 
     INNER JOIN #T ON #T.txt = X.txt AND #T.rn = X.rn 

Output

+----+----+-----+-------------------------+---------+-------------+ 
| id | rn | txt |   dt   | lagDiff | runningDiff | 
+----+----+-----+-------------------------+---------+-------------+ 
| 1 | 0 | t1 | 2017-01-01 00:01:00.000 |  0 |   0 | 
| 4 | 5 | t1 | 2017-01-01 01:04:00.000 |  12 |   63 | 
| 5 | 7 | t1 | 2017-01-01 02:10:00.000 |  65 |   129 | 
| 9 | 0 | t2 | 2017-01-01 00:03:00.000 |  0 |   0 | 
| 10 | 1 | t2 | 2017-01-01 01:04:00.000 |  61 |   61 | 
| 8 | 2 | t2 | 2017-01-01 02:10:00.000 |  66 |   127 | 
+----+----+-----+-------------------------+---------+-------------+  
+0

Danke Steve. Ich sollte erwähnen, bevor ich SQL Server 2008 R2 benutze und LAG Funktion ist nicht gut für mich: /. Danke trotzdem, ich kann viel von Ihrer Lösung lernen. – andrewp

+0

Ich habe das SQL so geändert, dass es 2008 kompatibel ist. Wir mussten nur die Funktionen FIRST_VALUE und LAG adressieren, die in der Population der temporären Tabelle verwendet wurden. Ich habe sie durch MIN und MAX mit einer modifizierten OVER-Klausel ersetzt. SELECT ID , ROW_NUMBER() OVER (PARTITION BY txt ORDER BY dt, id) -1 rn , txt , dt , DATEDIFF (MINUTE koaleszieren (MAX (dt) OVER (PARTITION BY txt ORDER BY dt, id ROWS ZWISCHEN UNTERGEBUNDENEM VORZUG UND 1 VORZUG), dt), dt) Diff , DATEDIFF (MINUTE, KOALESSE (MIN (dt) ÜBER (TEILUNG DURCH txt), dt), dt) RunningDiff VON wt – Steve

+0

Sorry über die Formatierung. scheint nicht in der Lage zu sein, Code in einen Kommentar einzufügen – Steve

Verwandte Themen