我将包括解释、函数和相关模式,并希望有人能够看到更好的编写函数的方法。这是SQL Server 2008。
首先,业务基础:想象一种医疗程序。执行该程序的医疗保健提供者发送一个或多个账单,每个账单可能有一个或多个项目(BillItems)。
该程序重新向另一方进行了再次结算。向第三方收取的费用可能是:
1.提供者的全部账单金额 2.提供者的全部账单金额加上Copay金额,或者 3.完全不同的金额(Rebill金额)
当前用于计算程序账单的函数考虑了所有三种情况:
CREATE FUNCTION [dbo].[fnProcTotalBilled] (@PROCEDUREID INT)
RETURNS MONEY AS
BEGIN
DECLARE @billed MONEY
SELECT @billed = (SELECT COALESCE((SELECT COALESCE(sum(bi.Amount),0)
FROM BillItems bi INNER JOIN Bills b ON b.BillID=bi.BillID
INNER JOIN Procedures p on p.ProcedureID=b.ProcedureID
WHERE b.ProcedureID=@PROCEDUREID
AND p.StatusID=3
AND b.HasCopay=0
AND b.Rebill=0),0))
-- the total of the provider's billing, with no copay and not rebilled
+
(SELECT COALESCE((SELECT sum(bi.Amount) + COALESCE(b.CopayAmt,0)
FROM BillItems bi INNER JOIN Bills b ON b.BillID=bi.BillID
INNER JOIN Procedures p on p.ProcedureID=b.ProcedureID
WHERE b.ProcedureID=@PROCEDUREID
AND p.StatusID=3
AND b.HasCopay=1
GROUP BY b.billid,b.CopayAmt),0))
-- the total of the provider's billing, plus a Copay amount
+
(SELECT COALESCE((SELECT sum(COALESCE(b.RebillAmt,0))
FROM Bills b
INNER JOIN Procedures p on p.ProcedureID=b.ProcedureID
WHERE b.ProcedureID=@PROCEDUREID
AND p.StatusID=3
AND b.Rebill=1),0))
-- the Rebill amount, instead of the provider's billing
RETURN @billed
END
我将省略该过程的DDL。可以说,它必须具有特定的状态(在函数中表示为p.StatusID = 3)。
这是账单及相关账单项的DDL:
CREATE TABLE dbo.Bills (
BillID int IDENTITY(1,1) NOT NULL,
InvoiceID int DEFAULT ((0)),
CaseID int NOT NULL,
ProcedureID int NOT NULL,
TherapyGroupID int DEFAULT ((0)) NOT NULL,
ProviderID int NOT NULL,
Description varchar(1000),
ServiceDescription varchar(255),
BillReferenceNumber varchar(100),
TreatmentDate datetime,
DateBilled datetime,
DateBillReceived datetime,
DateBillApproved datetime,
HasCopay bit DEFAULT ((0)) NOT NULL,
CopayAmt money,
Rebill bit DEFAULT ((0)) NOT NULL,
RebillAmt money,
IncludeInDemand bit DEFAULT ((1)) NOT NULL,
CreateDate datetime DEFAULT (getdate()) NOT NULL,
CreatedByID int,
ChangeDate datetime,
ChangeUserID int,
PRIMARY KEY (BillID)
);
CREATE TABLE dbo.BillItems (
BillItemID int IDENTITY(1,1) NOT NULL,
BillID int NOT NULL,
ItemDescription varchar(1000),
Amount money,
WillNotBePaid bit DEFAULT ((0)) NOT NULL,
CreateDate datetime DEFAULT (getdate()),
CreatedByID int,
ChangeDate datetime,
ChangeUserID varchar(25),
PRIMARY KEY (BillItemID)
);
我完全意识到这个函数有多么复杂;但我找不到另一种解决所有场景的方法。
我希望更优秀的SQL程序员或DBA能够看到更高效的解决方案。
非常感谢任何帮助。
谢谢,
汤姆
更新:
感谢大家的回复。我试图在评论中添加一些澄清,但我也会在这里这样做。
首先,定义:一个Procedure是Provider在单个服务日期提供的医疗服务。我们只关心一个Procedure的总计费用;多个人不会收到账单。
一个“Case”可以有多个Procedure。
通常,单个Procedure将有一个账单 - 但并非总是如此。一个账单可能有一个或多个BillItems。Copay(如果存在)将添加到BillItems的总和中。重发金额胜过一切。
性能问题出现在更高的层面上,当计算整个Case(多个Procedure)的总计时以及需要显示同时显示数百个Case的网格数据时。
我的查询是在Procedure级别进行的,因为描述问题更简单。
至于示例数据,在@Serpiton的SQL Fiddle中的数据是一个很好的、简洁的例子。非常感谢您提供它。
在审查答案时,我认为@Serpiton和@GarethD的CTE方法都是对我的原始方法的强有力改进。目前,我将使用CTE方法,仅为了避免处理SELECT的多个结果的必要性。
我修改了@Serpiton的CTE以在Case级别上工作。如果他或其他人能看一下它,我会很感激。在我的测试中,它运行良好,但我希望其他人也能看一看。
它的操作如下:
WITH Normal As (
SELECT b.BillID
, b.CaseID
, sum(coalesce(n.Amount * (1 - b.Rebill), 0)) Amount
FROM Procedures p
INNER JOIN Bills b ON p.ProcedureID = b.ProcedureID
LEFT JOIN BillItems n ON b.BillID = n.BillID
WHERE b.CaseID = 3444
AND p.StatusID = 3
GROUP BY b.CaseID,b.BillID, b.HasCopay
)
SELECT Amount = Sum(b.Amount)
+ Sum(Coalesce(c.CopayAmt, 0))
+ Sum(Coalesce(r.RebillAmt, 0))
FROM Normal b
LEFT JOIN Bills c ON b.BillID = c.BillID And c.HasCopay = 1
LEFT JOIN Bills r ON b.BillID = r.BillID And r.Rebill = 1
GROUP BY b.caseid
BillItems
(就像我的答案中所示)是否可以简化逻辑,消除对其他字段进行分组的需求,并消除对Bills
的两个额外连接的要求? - MatBailieEND
关键字。 - MatBailie