Ist es möglich, diese Mathematik in einer Ansicht durchzuführen?

7

Ich wurde beauftragt, eine Ansicht für einen Kunden zu erstellen. Insbesondere muss es in einer Ansicht sein . Es gibt jedoch einige Berechnungen, bei denen ich nicht sicher bin, wie ich sie in einer Ansicht ausführen soll. Ich weiß nicht, ob es überhaupt möglich ist. Aber andererseits ist mein Verstand schwach.

Ich verwende SQL Server 2008R2, daher OVER()funktionieren erweiterte Funktionen nicht.

Nehmen wir an, eine Person erhält 400 Dollar zum Ausgeben. Sie können mehr ausgeben, aber die ersten 400 Dollar sind kostenlos. Eine Spalte des Berichts enthält den Betrag, den die Person für etwas ausgegeben hat, und eine andere Spalte enthält den Gesamtbetrag, den die Person aus eigener Tasche bezahlen muss.

Für den ersten Datensatz im Bericht für diese Person hat eine Spalte einen Betrag, den sie ausgegeben hat, z. B. 50 US-Dollar, und eine zweite Spalte hat einen Betrag von 0 US-Dollar. Hinter den Kulissen müssen sie noch 350 Dollar ausgeben.

Der nächste Rekord hat die Person, die 300 Dollar ausgibt. In der zweiten Spalte wird weiterhin 0 US-Dollar angezeigt, und hinter den Kulissen sind die anfänglichen 400 US-Dollar jetzt 50 US-Dollar.

Der dritte Rekord für die Person zeigt, dass sie 75 Dollar ausgegeben hat, aber nur noch 50 Dollar von den anfänglichen 400 Dollar übrig sind. Die zweite Spalte sollte jetzt einen Wert von 25 USD enthalten. Sie haben die anfänglichen 400 Dollar aufgebraucht und geben jetzt ihr eigenes Geld aus.

Der vierte Datensatz zeigt, dass sie 40 US-Dollar ausgegeben haben. In der zweiten Spalte werden nun 65 US-Dollar angezeigt. usw...

Ich habe kurz über CTEs und Tabellenwertfunktionen und dergleichen gelesen, aber ist es möglich, sie in einer beliebigen Kombination zu verwenden, um das gewünschte Verhalten oben zu erzielen?

Unten finden Sie einen Beispielcode für die Struktur und die gewünschten Ergebnisse

CREATE TABLE Payroll (
    PersonID int,
    PlanCode varchar(10),
    Deduction int NULL
)
GO

INSERT INTO Payroll (PersonID, PlanCode, Deduction)
VALUES (1, 'Medical', 200)
  ,(1, 'Dental', 250)
  ,(1, 'Vision', 300)
  ,(2, 'Medical', 100)
  ,(2, 'Dental', 150)
  ,(2, 'Vision', 100)
  ,(2, 'Disability', 100)
  ,(2, 'Life', 140) 

Gewünschten Erfolge:

Geben Sie hier die Bildbeschreibung ein

Es kann sinnvoll sein, sich das OutOfPocketals vorzustellen TotalOutOfPocket.

Es gibt nichts Besseres als einen Zeitstempel in den Quelldaten für die Reihenfolge der Einträge. Die Reihenfolge ist nicht zu wichtig. Wenn eine Bestellung erfolgt, erfolgt diese auf der PlanCode.

Aufgrund unserer Einschränkungen und einer dritten Spalte, die nicht enthalten sein musste, sind keine doppelten Einträge möglich.

Jeff.Clark
quelle
Lesen Sie dies: Beste Ansätze zum Ausführen von Summen Es gibt mehrere Methoden, die in älteren Versionen vor der Version 2012 funktionieren. Versuchen Sie den Cursor, wenn Sie am effizientesten wollen. Ein Index auf (PersonID, PlanCode) INCLUDE (Deduction)würde den Abfragen helfen (je nachdem, was Sie verwenden möchten).
Ypercubeᵀᴹ

Antworten:

2

Dies wird in PlanCode sortiert. Wenn Sie Duplikate haben, verwenden Sie eine row_number ().
Wenn Sie eine bestimmte Reihenfolge benötigen, muss diese Reihenfolge in der Tabelle enthalten sein

  select PersonID, PlanCode, Deduction, [sum]
       , case when [sum] < 400 then 0 else [sum] - 400 end as oop
  from
  ( select p1.PersonID, p1.PlanCode, p1.Deduction 
         , ( select sum(p2.Deduction) 
               from payroll p2  
              where p2.PersonID  = p1.PersonID 
                and p2.PlanCode <= p1.PlanCode ) as [sum] 
    from payroll p1 
  ) tt
  order by tt.PersonID, tt.PlanCode 

oder

  select p1.PersonID, p1.PlanCode, p1.Deduction 
       , case  when sum(p2.Deduction) < 400 then 0 else sum(p2.Deduction) - 400 end as OOP
  from payroll p1 
  join payroll p2  
        on p2.PersonID  = p1.PersonID 
       and p2.PlanCode <= p1.PlanCode  
 group by p1.PersonID, p1.PlanCode, p1.Deduction 
Paparazzo
quelle
Aaaaaaaund ich habe gerade versehentlich das Kopfgeld an Jyao vergeben. Ich fange an zu denken, dass ich ohne Kaffee nicht funktionieren kann. Nebenbei bemerkt, eine Anforderung hat sich geändert, wodurch sich die Frage so geändert hat, dass ich nicht herausfinden konnte, wie Sie Ihren Code mit der neuen Anforderung verwenden sollen. Wenn Sie einen Blick darauf werfen möchten, habe ich eine Frage auf CodeReview gestellt. ( codereview.stackexchange.com/questions/143899/… )
Jeff.Clark
6

Etwas in diese Richtung vielleicht? Mit der OVERKlausel für können Sie Summen ausführen SUM.

CREATE TABLE Expenses (
    expense_id int NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    amount decimal(19,5)
)

INSERT INTO Expenses (amount) VALUES (50), (300), (75), (40)
GO

WITH running_total AS (
    SELECT
        expense_id,
        amount,
        SUM(amount) OVER (ORDER BY expense_id ROWS UNBOUNDED PRECEDING) AS total
    FROM Expenses
)
SELECT
    expense_id,
    amount,
    total,
    CASE WHEN total > 400 THEN total - 400 ELSE 0 END AS out_of_pocket_total
FROM running_total
ORDER BY expense_id
db2
quelle
Um dies zu einer ANSICHT zu machen, fügen Sie CREATE VIEW [ViewName] ASvor der WITH running_totalZeile hinzu. Sie müssen auch die ORDER BYam Ende entfernen ; ANSICHTEN werden ORDER BYin der Definition nicht unterstützt . Sie können dieselbe Anweisung anwenden, wenn Sie die ANSICHT abfragen.
Steven Hibble
ALAS, ich benutze SqlServer 2008 R2, so dass alles über OVER (Partition By Order By) nicht funktioniert :(
Jeff.Clark
1
@ Jeff.Clark Bummer! Sie können wahrscheinlich ähnliche Ergebnisse erzielen, indem Sie SUM () durch eine korrelierte Unterabfrage ersetzen, was möglicherweise zu Lasten einer gewissen Leistung geht.
db2
5

Das folgende Skript wird streng auf Ihren Beispieldaten basieren (und seine Tabellenstruktur) und ist für SQL Server 2008 runnable.

; with c as (
select personid, plancode, deduction
     , rownum=ROW_NUMBER() over (partition by PersonID order by personid )
from dbo.Payroll
)
, c2 as (
select personid, plancode, deduction, T.ytd
from c
cross apply (select ytd = sum(deduction) 
             from c cc 
             where c.PersonID = cc.PersonID 
             and c.rownum >= cc.rownum) T(ytd)
)
select personid, plancode, deduction, 
       OutOfPoket = case when 400 > ytd then 0 else ytd-400 end
from c2 

Geben Sie hier die Bildbeschreibung ein

Diese Frage kann einfacher zu behandeln sein, wenn die Tabelle [PayRoll] einen Primärschlüssel hat. Ich habe den ganzen Code hier eingefügt:

use tempdb
drop table dbo.Payroll
CREATE TABLE Payroll (
    id int identity primary key, -- assume there is a PK here
    PersonID int,
    PlanCode varchar(10),
    Deduction int NULL
)
GO

INSERT INTO Payroll (PersonID, PlanCode, Deduction)
VALUES (1, 'Medical', 200)
  ,(1, 'Dental', 250)
  ,(1, 'Vision', 300)
  ,(1, 'Medical', 111) -- additional row (though not needed as per comments by original owner, but just for fun of adding more complexity)
  ,(2, 'Medical', 100)
  ,(2, 'Dental', 150)
  ,(2, 'Vision', 100)
  ,(2, 'Disability', 100)
  ,(2, 'Life', 140) 
go


-- easier solution
select personid, plancode, deduction
,OutOfPocket= case when T.ytd > 400 then t.Ytd-400 else 0 end
from dbo.Payroll c
cross apply (
            select ytd = sum(deduction) 
            from dbo.Payroll cc 
            where c.PersonID = cc.PersonID and c.ID >=cc.ID)  T(ytd)

Das Ergebnis ist: Geben Sie hier die Bildbeschreibung ein

jyao
quelle
Oh, interessant. Die tatsächlichen Daten haben einen Primärschlüssel. In meiner Unwissenheit dachte ich nicht, dass es wichtig war. Diese Unterscheidung wird mir in Zukunft gut in Erinnerung bleiben. Ich werde mich weiterhin mit der Implementierung dieser Lösungen befassen. Wenn Sie können, behalten Sie sowohl die mit als auch ohne Primärschlüssellösung bei, damit ich sie für mein eigenes Lernen vergleichen kann.
Jeff.Clark