Laufende Summe, die zurückgesetzt wird, wenn sich der Kunde ändert (vorhandene RowNumbers beibehalten) - Versuch eingeschlossen

7

Ich habe eine Tabelle, in der die RowNumber von wesentlicher Bedeutung ist. Es verfügt über eine Reihe von Clients und eine laufende Summe für diesen Client, die bei jeder Änderung des Clients zurückgesetzt wird. z.B

client  rownr   amount  runtotal
Company A   1   1.00    1.00
Company A   2   1.00    2.00  1+1 = 2
Company A   3   5.00    7.00  2+5 = 7
Company B   4   3.00    3.00  Reset Because Customer B <> Previous Row Customer A
Company A   5   1.00    1.00  Reset Because Customer A <> Previous Row Customer B
Company B   6   2.00    2.00  Reset Because Customer B <> Previous Row Customer A
Company B   7   1.00    3.00  2+1 = 3
Company B   8   5.00    8.00  3+5 = 8

Ich habe versucht, Partition zu verwenden, aber es gruppiert immer Client A und Client B zusammen - es entfernt den entscheidenden Teil der Zeilennummern.

Hilfe bitte?

 drop table #Checks
CREATE TABLE #Checks
(
  Client VARCHAR(32),
  RowNr int,
  Amount DECIMAL(12,2),
  RunTotal DECIMAL(12,2),
  Part int
);

INSERT #Checks(Client, RowNr, Amount)
          SELECT 'Company A', '1', 1
UNION ALL SELECT 'Company A', '2', 1
UNION ALL SELECT 'Company A', '3', 5
UNION ALL SELECT 'Company B', '4', 3
UNION ALL SELECT 'Company A', '5', 1
UNION ALL SELECT 'Company B', '6', 2
UNION ALL SELECT 'Company B', '7', 1
UNION ALL SELECT 'Company B', '8', 5;

-- gets the first entries per client - these amounts are 
-- the base amounts and the other entries are tallied up

with cte as (
            select
                    c1.client
                  , c1.amount
                  , c1.rowNr
                  , case when c1.client <> c2.client then c1.amount else null end amt
            from #Checks as c1
            left join #Checks as C2 on c1.rownr = (c2.rownr + 1)
  )

select
client
, rownr
, amount
, case when isnull(amt,0) > 0 then amt else total end as runtotal
from cte
cross apply (
                select
                       sum(x.amount) as Total
                from cte as x
                where x.rownr <= cte.rownr and cte.client = x.client
            ) as rt
;

Drop table #Checks
Peter PitLock
quelle

Antworten:

4

Wie wäre es mit:

SELECT c.client, c.rownr, c.amount
     , runtotal = SUM(e.amount)
FROM   #Checks c
JOIN   #Checks e ON e.RowNr <= c.RowNr AND e.Client = c.Client -- row from c (note <= not <) and all earlier rows
LEFT OUTER JOIN -- rows between with different cli
       #Checks d ON d.RowNr < c.RowNr AND d.RowNr > e.RowNr AND d.Client<>c.Client
WHERE d.RowNr IS NULL -- drop where there is a row between the one from c and the one from e with different client
GROUP BY c.client, c.rownr, c.amount
ORDER BY c.rownr

Dieser erste Join kann für ein großes Dataset teuer sein, wenn ein Index auf dem Client vorhanden ist (um überhaupt effizient zu sein, benötigen Sie einen Index auf dem Client, rownr und einen auf rownr (obwohl ich davon ausgehe, dass dies Ihre PK ist, die eine impliziert) Index bereits)) ist nicht sehr selektiv (wenn die Vielfalt der Daten in der Client-Spalte und die Verteilung in Bezug auf Rown bedeutet, dass ein darauf basierender Index nicht sehr selektiv ist, ist dieser erste Join einem kartesischen Produkt so nahe wie macht keine Chancen).

David Spillett
quelle
6

Dies sollte tun, was Sie wollen:

WITH CTE AS
(
    SELECT  *, 
            RN = ROW_NUMBER() OVER(PARTITION BY Client ORDER BY RowNr)
    FROM #Checks
), CTE2 AS
(
    SELECT  *, 
            RN2 = RowNr - RN 
    FROM CTE 
)
SELECT A.Client, A.RowNr, A.Amount, B.RunTotal
FROM CTE2 A
CROSS APPLY (SELECT SUM(Amount) RunTotal
             FROM CTE2
             WHERE Client = A.Client
             AND RN2 = A.RN2
             AND RowNr <= A.RowNr) B
ORDER BY RowNr

Ergebnisse:

╔═══════════╦═══════╦════════╦══════════╗
  Client    RowNr  Amount  RunTotal 
╠═══════════╬═══════╬════════╬══════════╣
 Company A      1  1.00    1.00     
 Company A      2  1.00    2.00     
 Company A      3  5.00    7.00     
 Company B      4  3.00    3.00     
 Company A      5  1.00    1.00     
 Company B      6  2.00    2.00     
 Company B      7  1.00    3.00     
 Company B      8  5.00    8.00     
╚═══════════╩═══════╩════════╩══════════╝
Lamak
quelle
4

SQL Fiddle

MS SQL Server 2008 Schema-Setup :

CREATE TABLE dbo.Checks
(
  Client VARCHAR(32),
  RowNr int,
  Amount DECIMAL(12,2),
  RunTotal DECIMAL(12,2),
  Part int
);

INSERT dbo.Checks(Client, RowNr, Amount)
          SELECT 'Company A', '1', 1
UNION ALL SELECT 'Company A', '2', 1
UNION ALL SELECT 'Company A', '3', 5
UNION ALL SELECT 'Company B', '4', 3
UNION ALL SELECT 'Company A', '5', 1
UNION ALL SELECT 'Company B', '6', 2
UNION ALL SELECT 'Company B', '7', 1
UNION ALL SELECT 'Company B', '8', 5;

Abfrage 1 :

WITH Islands AS
(
  SELECT Client, RowNr, Amount, RowNr-ROW_NUMBER()OVER(PARTITION BY Client ORDER BY RowNr) IslId
  FROM Checks
)


SELECT *
FROM Islands I1
CROSS APPLY(
  SELECT SUM(Amount) RunningTotal
  FROM Islands I2
  WHERE I1.Client = I2.Client
  AND I1.IslId = I2.IslId
  AND I1.RowNr >= I2.RowNr
)C

Ergebnisse :

|    CLIENT | ROWNR | AMOUNT | ISLID | RUNNINGTOTAL |
-----------------------------------------------------
| Company A |     1 |      1 |     0 |            1 |
| Company A |     2 |      1 |     0 |            2 |
| Company A |     3 |      5 |     0 |            7 |
| Company A |     5 |      1 |     1 |            1 |
| Company B |     4 |      3 |     3 |            3 |
| Company B |     6 |      2 |     4 |            2 |
| Company B |     7 |      1 |     4 |            3 |
| Company B |     8 |      5 |     4 |            8 |
Sebastian Meine
quelle