SQL查询显示每月支付的费用

时间:2010-04-18 11:59:56

标签: sql sql-server-2005 tsql

我正在寻找帮助来创建查询,以解决以下问题:

让我们想象一下这行:

Name    StartDate   EndDate     Paid
James   10-10-2010  17-02-2011  860

并且按照要求继承了表的架构:

payment_details (name VARCHAR(50) NOT NULL, 
                 start_date DATETIME NOT NULL, 
                 end_date DATETIME NOT NULL,
                 paidFLOAT NOT NULL)

现在我需要一种方法来分割这一行,所以我可以看到他每个月支付的费用,对于他的期间,一个返回的查询:

Name    Year    Month   Paid
James   2010    10      172
James   2010    11      172
James   2010    12      172
James   2011    01      172
James   2011    02      172

有许多不同的客户使用不同的StartDate / EndDate和支付金额,因此查询必须处理此问题。如何在SQL(MS SQL Server 2005)中执行此操作?

非常感谢帮助!

4 个答案:

答案 0 :(得分:3)

你需要:

  • 为每个名称生成一个行(使用Numbers表)
  • 生成月份差值作为除数(DATEDIFF使用月末作为月份边界)
  • 假设end_date> = start_date(你有CHECK约束吗?)以避免被零除错误

你当然可能需要调整它......

--DROP TABLE #numbers 
--DROP TABLE #payment_details
CREATE TABLE #numbers (num smallint PRIMARY KEY)

CREATE TABLE #payment_details (name VARCHAR(50) NOT NULL, 
                 start_date DATETIME NOT NULL, 
                 end_date DATETIME NOT NULL,
                 paid FLOAT NOT NULL)

INSERT #payment_details VALUES ('James', '20101010', '20110217', 860)
INSERT #payment_details VALUES ('Jane', '20101110', '20110117', 900)
INSERT #payment_details VALUES ('John', '20101128', '20101128', 500)

INSERT #numbers
SELECT TOP 1000
    ROW_NUMBER() OVER (ORDER BY c1.object_id) -1
FROM
    sys.columns c1 CROSS JOIN sys.columns c2

SELECT
    P.name,
    DATEPART(year, DATEADD(month, N.Num, start_date)),
    DATEPART(month, DATEADD(month, N.Num, start_date)),
    P.paid / (DATEDIFF(month, start_date, end_date) + 1)
FROM
    #payment_details P
    JOIN
    #numbers N ON DATEDIFF(month, start_date, end_date) >= N.num

答案 1 :(得分:1)

您可能需要日期表,包含合理期间的每个日期。然后,您可以将其加入付款,计算付款金额作为总金额除以月份的期间长度。

select p.name, d.year, d.month, p.paid/(datediff(m, p.startdate, p.enddate) + 1)
from (
    select year(date) as year, month(date) as month, min(date) as monthbegin, max(date) as monthend
    from datestable
    group by year(date), month(date)
) d
left join payment_detail p on d.monthbegin<p.enddate and d.monthend>p.startdate

我希望我的加入条件合适,随意纠正(我没有可能在这里测试)。

答案 2 :(得分:0)

关于我在直接SQL中看到这样做的唯一方法是使用另一个名为Periods的表,其中包含所有年份和月份,如下所示:

Year    Month
2010      10
2010      11
2010      12
2011       1
2011       2
2011       3
2011       4

依旧......

现在,您可以将付款明细表与该表一起加入:

SELECT PD.[name], Per.Year, Per.Month, 
    PD.payed / DateDiff(mm, PD.start_date, PD.end_date) AS Payed
FROM payment_details PD
    INNER JOIN Periods Per On 
        Per.Year >= DatePart(yy, start_date) AND Per.Month >= DatePart(mm, start_date)
    AND Per.Year <= DatePart(yy, end_date) AND Per.Month <= DatePart(mm, end_date)
ORDER BY PD.[name], Per.Year, Per.Month

否则,您可能需要创建一个存储过程并逐步完成这些月份并动态创建结果数据集。

答案 3 :(得分:0)

只是为了提供一些横向思考,你每个月需要一行吗?根据您是否,例如,从中计算某些内容,或者您​​要为某人审核创建报告,您可能不需要创建6行,其中唯一不同的数据是月/年。这也假设付款在几个月内均匀分配。通过选择姓名,开始年份,开始月份,结束年份,结束月份,然后将支付的总额除以((年差* 12)+月差异)来确定每月付款,可以很容易地写出来。

根据用途的不同,沿着这些线条的东西可能看起来更干净,而且有人打算将它打印出来的纸张少得多。 :)