私はクライアントのビューを作成する任務を負っています。具体的にはビューにある必要があります。ただし、ビュー内で実行する方法がわからない数学があります。それが可能かどうかさえわかりません。しかし、再び、私の心は弱いです。
SQL Server 2008R2を使用しているため、高度なOVER()
機能が動作しません。
人が使うために400ドルを与えられているとしましょう。彼らはもっと使うことができますが、最初の$ 400は無料です。レポートの1つの列には、その人が何かに費やした金額が表示され、別の列には、その人が自分のポケットから支払う必要がある合計金額が表示されます。
したがって、この人のレポートの最初のレコードについては、1つの列には50ドルなどの費やした金額があり、2番目の列には0ドルがあります。舞台裏では、彼らはまだ350ドルを費やすことができます。
次のレコードには、300ドルを費やした人がいます。 2列目にはまだ$ 0が表示され、舞台裏では最初の$ 400が$ 50になっています。
その人の3番目のレコードは、彼らが$ 75ドルを費やしたことを示していますが、最初の$ 400から$ 50しか残っていません。 2列目には25ドルの値が含まれているはずです。彼らは最初の$ 400を使い果たし、現在自分のお金を使っています。
4番目のレコードは40ドルを費やしたことを示しているため、2列目には65ドルが表示されます。等...
CTEとテーブル値関数などについて簡単に読みましたが、これらを任意の組み合わせで使用して、上記の望ましい動作を実現できますか?
以下は、構造と望ましい結果のサンプルコードです。
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)
望ましい結果:
OutOfPocket
をTotalOutOfPocket
と考えることは理にかなっています。
ソースデータには、エントリの順序付けのためのタイムスタンプのようなものはありません。順序はあまり重要ではありません。順序付けが行われた場合は、PlanCode
になります。
制約と、含める必要のなかった3番目の列に基づいて、重複したエントリが発生することはありません。
これはPlanCodeでソートされます-重複がある場合は、row_number()を使用します
特定の注文が必要な場合は、その注文を表に含める必要があります
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
または
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
これらの線に沿った何か? OVER
の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
次のスクリプトは、サンプルデータ(およびそのテーブル構造)に基づく厳密にであり、SQL Server 2008で実行できます。
; 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
この質問は、テーブル[PayRoll]に主キーがある場合に、より簡単な方法で処理できます。ここにコード全体を入れます:
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)