2015-10-08 9 views
5

Sorry für schlechtes Thema, aber ich war nicht sicher, wie ich es zu nennen ..SUM Lauf in T-SQL

ich eine Tabelle wie folgt aussehen haben:

+-----++-----+ 
| Id ||Count| 
+-----++-----+ 
| 1 || 1 | 
+-----++-----+ 
| 2 || 5 | 
+-----++-----+ 
| 3 || 8 | 
+-----++-----+ 
| 4 || 3 | 
+-----++-----+ 
| 5 || 6 | 
+-----++-----+ 
| 6 || 8 | 
+-----++-----+ 
| 7 || 3 | 
+-----++-----+ 
| 8 || 1 | 
+-----++-----+ 

Ich versuche zu machen Wählen Sie aus dieser Tabelle, wo jedes Mal, wenn die Summe von Zeile1 + Zeile2 + Zeile3 (usw.) 10 erreicht, dann ist es ein "HIT", und die Zählung beginnt von neuem.

Gewünscht Ausgabe:

+-----++-----++-----+ 
| Id ||Count|| HIT | 
+-----++-----++-----+ 
| 1 || 1 || N | Count = 1 
+-----++-----++-----+ 
| 2 || 5 || N | Count = 6 
+-----++-----++-----+ 
| 3 || 8 || Y | Count = 14 (over 10) 
+-----++-----++-----+ 
| 4 || 3 || N | Count = 3 
+-----++-----++-----+ 
| 5 || 6 || N | Count = 9 
+-----++-----++-----+ 
| 6 || 8 || Y | Count = 17 (over 10..) 
+-----++-----++-----+ 
| 7 || 3 || N | Count = 3 
+-----++-----++-----+ 
| 8 || 1 || N | Count = 4 
+-----++-----++-----+ 

Wie dies tue ich, und mit dem besten Leistung? Ich habe keine Ahnung ..

+0

[siehe hier] (http://stackoverflow.com/a/31497897/3094533) –

+0

Interessanter Gedanke über die Verwendung von dichtem_rank(). Vielleicht würde das funktionieren. Ansonsten befürchte ich einen gespeicherten Proc mit einem Cursor ist wahrscheinlich was benötigt wird. –

+0

Wenn 'dense_rank()' nicht den Trick macht, könnten Sie dies auch mit einer rekursiven Ansicht tun. Ich glaube, dass 'dose_rank()' Route besser optimiert wäre. – JNevill

Antwort

3

Sie Recursive Queries

Bitte beachten Sie die folgende Abfrage unter der Annahme der ID-Wert sind alle in der Reihenfolge, sonst verwenden könnte, bitte ROW_NUMBER() verwenden eine neue ID

WITH cte AS (
    SELECT id, [Count], [Count] AS total_count 
    FROM Table1 
    WHERE id = 1 
    UNION ALL 
    SELECT t2.id,t2.[Count], CASE WHEN t1.total_count >= 10 THEN t2.[Count] ELSE t1.total_count + t2.[Count] END 
    FROM Table1 t2 
    INNER JOIN cte t1 
    ON t2.id = t1.id + 1 
) 
SELECT * 
FROM cte 
ORDER BY id 

SQL Fiddle zu erstellen

1

Ich hoffe wirklich, dass uns jemand zeigen kann, wenn es möglich ist, dies mit einfachen Fensterfunktionen zu tun. Das ist die wahre Herausforderung.

In der Zwischenzeit, hier ist, wie ich es mit Rekursion tun würde. Dies behandelt Lücken in der Sequenz und behandelt den Kantenfall der ersten Zeile, die bereits >= 10 ist.

Ich fügte auch den maxrecursion Hinweis hinzu, um das Standardrekursionslimit zu entfernen. Aber ich weiß ehrlich nicht, wie gut es mit größeren Datenmengen laufen wird.

with NumberedRows as (
    select Id, Cnt, 
     row_number() over(order by id) as rn 
    from CountTable 
), RecursiveCTE as (
    select Id, Cnt, rn, 
     case when Cnt >= 10 then 0 else Cnt end as CumulativeSum, 
     case when Cnt >= 10 then 'Y' else 'N' end as hit 
    from NumberedRows 
    where rn = 1 
    union all 
    select n.Id, n.Cnt, n.rn, 
     case when (n.Cnt + r.CumulativeSum) >= 10 then 0 else n.Cnt + r.CumulativeSum end as CumulativeSum, 
     case when (n.Cnt + r.CumulativeSum) >= 10 then 'Y' else 'N' end as hit 
    from RecursiveCTE r 
    join NumberedRows n 
     on n.rn = r.rn + 1 
) 
select Id, Cnt, hit 
from RecursiveCTE 
order by Id 
option (maxrecursion 0) 

SQLFiddle Demo

4

Dieses für einen Kommentar ist zu lang.

Sie können dies nicht mit Fenster-/Analysefunktionen tun, da die Haltepunkte nicht im Voraus bekannt sind. Manchmal ist es möglich, die Haltepunkte zu berechnen. In diesem Fall hängen die Haltepunkte jedoch von einer nichtlinearen Funktion der vorherigen Werte ab (ich kann mir im Moment kein besseres Wort als "nichtlinear" vorstellen). Das heißt, dass manchmal das Hinzufügen von "1" zu einem früheren Wert keinen Einfluss auf die Berechnung der aktuellen Zeile hat. Manchmal hat es eine große Wirkung. Die Implikation ist, dass die Berechnung am Anfang beginnen und durch die Daten iterieren muss.

Eine kleine Modifikation des Problems wäre lösbar mit solchen Funktionen. Wenn das Problem stattdessen darin bestünde, den überschüssigen Betrag für jede Gruppe zu übertragen (anstatt die Summe neu zu starten), wäre das Problem lösbar, indem kumulative Summen (und einige andere Tricks) verwendet würden.

Rekursive Abfragen (die andere bereitgestellt haben) oder eine sequenzielle Operation ist der beste Weg, dieses Problem anzugehen. Leider hat es keine Set-basierte Methode, um es zu lösen.

1

Wie über diese mit laufenden Summen:

DECLARE @Data TABLE(
    Id INT 
    ,SubTotal INT 
) 


INSERT INTO @Data 
    VALUES(1, 5) 

INSERT INTO @Data 
    VALUES(2, 3) 

INSERT INTO @Data 
    VALUES(3, 4) 

INSERT INTO @Data 
    VALUES(4, 4) 

INSERT INTO @Data 
    VALUES(5, 7) 

DECLARE @RunningTotal INT = 0 
DECLARE @HitCount INT = 0  

SELECT 
     @RunningTotal = CASE WHEN @RunningTotal < 10 THEN @RunningTotal + SubTotal ELSE SubTotal END 
     ,@HitCount = @HitCount + CASE WHEN @RunningTotal >= 10 THEN 1 ELSE 0 END 
     FROM @Data ORDER BY Id 

SELECT @HitCount -- Outputs 2 

Nachdem wieder liest die Frage sehe ich nicht die erforderliche Leistung nicht erfüllt - ich werde die Antwort lassen, wie es von Nutzen zu jemandem sein kann, suchen für ein Beispiel einer laufenden Gesamtlösung für diese Art von Problem, bei der nicht jede Zeile mit einem Y oder einem N markiert sein muss.