正在运行的合计列的复杂SQL查询

正在运行的合计列的复杂SQL查询,sql,sql-server,Sql,Sql Server,我试图在SQLServer2008中解决一个相当复杂的查询。我想从这里的SQL专家那里得到一些信息 假设我有一个包含以下字段的付款表: PaymentID int, 客户ID int, 付款日期日期时间, 金额小数 因此,从本质上讲,它是一个客户在特定日期付款的表格。需要注意的重要一点是,在某些情况下,支付金额可能是负值。因此,随着时间的推移,任何特定客户支付的总金额都可能上升或下降 我们试图找出的是计算每个客户支付的总金额的高点的SQL 所以,如果弗雷德付了3笔钱:第一笔5美元,第二笔5美元,

我试图在SQLServer2008中解决一个相当复杂的查询。我想从这里的SQL专家那里得到一些信息

假设我有一个包含以下字段的付款表:

PaymentID int, 客户ID int, 付款日期日期时间, 金额小数

因此,从本质上讲,它是一个客户在特定日期付款的表格。需要注意的重要一点是,在某些情况下,支付金额可能是负值。因此,随着时间的推移,任何特定客户支付的总金额都可能上升或下降

我们试图找出的是计算每个客户支付的总金额的高点的SQL

所以,如果弗雷德付了3笔钱:第一笔5美元,第二笔5美元,第三笔3美元。报告将显示,弗雷德第二次付款的最高总付款额为10美元,最终付款额为7美元

我们需要为10万名客户运行此报告,这些客户可能每人支付了100至1000笔款项,所以它必须快速运行


有没有一种好方法可以在不将运行总数存储在数据库中的情况下构造此查询?如果可能的话,我们希望避免存储预先计算的值。

该操作在每个客户的付款数量上是线性的。因此,你必须检查每一笔付款,保持一个总金额和一个高水位线,在所有付款结束时,你将得到你的答案。无论您是在CLR存储过程中这样做,还是使用游标或临时表或其他什么,我都会立即想到,这可能不会很快

如果你必须一遍又一遍地运行这个报告,你应该认真考虑保持高水印字段,并在付款时更新它。这样,您的报告将变得微不足道——但这就是数据集市的用途

list = list of amounts ordered by date
foreach in list as amount
  running += amount
  if running >= high
    high = running
为了保持快速,您需要在触发器上使用金额递增的运行总数,并且每个客户的高值也可以通过触发器更新,以使重新查询更加简单


我认为如果没有代码,您无法完成这类工作存储过程是代码

您的问题似乎是:

SELECT CustomerID, SUM(Ammount) FROM table WHERE Amount > 0 GROUP BY CustomerID
SELECT CustomerID, SUM(Ammount) FROM table GROUP BY CustomerID
然而,我想你的意思是你想要一张这样的桌子

Customer  Payment  HighPoint  RunningTotal
123       5        5          5
123       5        10         10
123       -3       10         7
在这种情况下,我将使用上面的两个选项创建一个视图,以便该视图类似于

SELECT CusotmerID, 
  PaymentDate, 
  Ammount, 
  (SELECT SUM(Ammount) 
    FROM table as ALIAS 
    WHERE ALIAS.Amount > 0 
      AND ALIAS.PaymentDate <= PaymentDate 
      AND ALIAS.CustomerID = CustomerID), 
  (SELECT SUM(Ammount) 
    FROM table as ALIAS 
    WHERE ALIAS.CustomerID = CustomerID 
    AND ALIAS.PaymentDate <= PaymentDate)
FROM table

此外,您可以在表的数量列上考虑非唯一索引以加快视图。

< P>如Andomar的答案。您可以计算每次付款的总金额。然后找到最大峰值付款

with
rt as (
  select
    Payments.*,
    isnull(sum(p.Amount), 0) + Payments.Amount as running
  from
    Payments
    left outer join Payments p on Payments.CustomerID = p.CustomerID
      and p.PaymentDate <= Payments.PaymentDate
      and p.PaymentID < Payments.PaymentID
),
highest as
(
  select
    CustomerID, PaymentID, running as peak_paid
  from
    rt
  where
    PaymentID = (select top 1 rt2.PaymentID 
        from rt rt2 
        where rt2.CustomerID = rt.CustomerID
        order by rt2.running desc, rt2.PaymentDate, rt2.PaymentID)
)

select
  *,
  (select sum(amount) from Payments where Payments.CustomerID = highest.CustomerID) as total_paid  
from
  highest;
然而,由于你有大约100万的付款,这可能是相当缓慢的。正如其他人所说,您希望将CustomerID、PaymentID和peak_paid存储在一个单独的表中。此表可以在每次付款插入时更新,也可以作为sqljob更新


已更新查询以使用联接而不是子查询。由于PaymentDate没有时间,因此我根据PaymentId筛选出同一天的多笔付款。

作为子查询的替代方法,您可以使用正在运行的总计查询。这是我为这个案子准备的。首先创建一些测试数据:

create table #payments (
    paymentid int identity,
    customerid int,
    paymentdate datetime,
    amount decimal
)

insert into #payments (customerid,paymentdate,amount) values (1,'2009-01-01',1.00)
insert into #payments (customerid,paymentdate,amount) values (1,'2009-01-02',2.00)
insert into #payments (customerid,paymentdate,amount) values (1,'2009-01-03',-1.00)
insert into #payments (customerid,paymentdate,amount) values (1,'2009-01-04',2.00)
insert into #payments (customerid,paymentdate,amount) values (1,'2009-01-05',-3.00)
insert into #payments (customerid,paymentdate,amount) values (2,'2009-01-01',10.00)
insert into #payments (customerid,paymentdate,amount) values (2,'2009-01-02',-5.00)
insert into #payments (customerid,paymentdate,amount) values (2,'2009-01-03',7.00)
现在,您可以执行running total查询,该查询计算每个客户每次付款后的余额:

select cur.customerid, cur.paymentdate, sum(prev.amount)
from #payments cur
inner join #payments prev
    on cur.customerid = prev.customerid
    and cur.paymentdate >= prev.paymentdate
group by cur.customerid, cur.paymentdate
这将生成以下数据:

Customer  Paymentdate        Balance after payment
1         2009.01.01         1
1         2009.01.02         3
1         2009.01.03         2
1         2009.01.04         4
1         2009.01.05         1
2         2009.01.01         10
2         2009.01.02         5
2         2009.01.03         12
要查看最大值,您可以对正在运行的total查询进行分组:

select customerid, max(balance)
from (
    select cur.customerid, cur.paymentdate, balance = sum(prev.amount)
    from #payments cur
    inner join #payments prev
        on cur.customerid = prev.customerid
        and cur.paymentdate >= prev.paymentdate
    group by cur.customerid, cur.paymentdate
) runningtotal
group by customerid
其中:

Customer   Max balance
1          4
2          12

希望这是有用的。

这根本不能回答问题。它怎么不能回答问题呢?wtf。。。你编辑了吗?PaymentDate甚至不在您的选择中,更不用说任何清晰的格式预注释了。。那么bug呢?我做了编辑、复制和粘贴,然后意识到我抓错了东西。哈哈,这就解释了困惑!JP,谢谢你。我从未尝试过CLR存储过程。我的理解是,一般来说,对于这样的事情,您希望避免CLR,因为它不会像TSQL优化的那样快。我想我们最终会把预先计算好的数据存储在某个地方。卢,实际上我对使用存储过程或返回数据的函数的想法很好。我想要避免的是在存储过程中使用游标。谢谢,dotjoe,看起来不错。该报告运行不太频繁,因此可能值得对该查询进行基准测试。我在这个查询中看到的一个问题是,如果一个客户在一天内进行了多次付款(这种情况经常发生),并且我们没有在付款日期存储时间组件,那么计算运行总金额就不起作用。细节。。我们可以从中找到一些答案。如果PaymentID是自动递增的,也许你可以使用它?谢谢你,安多玛。我来试一试。我喜欢这种付款方式。我仍然认为MS应该支持sumx在xx之前超额订购。这里有一个要分区的列,就不能用它吗?