我正在使用SQL Server 2005.我有两个表 -
1. tbl_Customer
- CustId INT,
- Name VARCHAR(40)
2. tbl_Transaction
- TransId INT,
- CustId INT,
- TranDate DATETIME
- DebitAmount NUMERIC(8,2),
- CreditAmount NUMERIC(8,2)
tbl_Customer
的数据CustId Name
1 ST
2 JS
3 MA
tbl_Transaction
的数据TransId CustId Tdate DtAmt CrAmt
101 1 1/1/2012 250 100
102 1 1/2/2012 0 100
103 1 1/2/2012 0 50
104 2 1/2/2012 400 200
105 2 1/3/2012 0 150
106 2 1/3/2012 0 40
107 2 1/4/2012 0 10
108 1 1/1/2012 350 50
109 1 1/2/2012 0 200
110 1 1/2/2012 0 100
111 2 1/10/2012 500 300
112 2 1/10/2012 0 120
我想从最后的零余额开始找到每个客户的余额。第一行的余额是(DebitAmount - CreditAmount),然后是下一行的余额(Balance - CreditAmount)。所以在查询之后结果应该如下所示 -
TransId CustId Transdate DtAmt CrAmt Balance
108 1 1/1/2012 350 50 300
109 1 1/2/2012 0 200 100
110 1 1/2/2012 0 100 0
111 2 1/10/2012 500 300 200
112 2 1/11/2012 0 120 80
现在主要的是,我想搜索客户并在结果中显示每个客户的最新余额。如果没有余额(tbl_transaction中没有可用的事务),则余额应为空。所以最终的结果应该是这样的。
CustId Name Balance
1 ST 0
2 JS 80
3 MA NULL
我希望我已经很清楚地阐述了我想要实现的目标。请告诉我如何实现这一目标。我是SQL新手,学习RDBMS的位和字节。提前谢谢。
仅限Ritesh
答案 0 :(得分:2)
以下应该可行,我不是100%在中间查询上获取每个中间值,但我相信它应该有效。
设置结果集:
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CustId ORDER BY TransDate) AS TransactionOrderNum
INTO #TransactionOrder
FROM tbl_Transaction
我认为这种交叉适用应该有效,但我承认我不是100%:
SELECT #TransactionOrder.*, CustomerSum AS Balance
FROM #TransactionOrder
CROSS APPLY
(
SELECT SUM(
CASE WHEN TransactionOrderNum = 1 THEN DtAmt - CrAmt
ELSE (CrAmt * -1) END
) AS CustomerSum
FROM #TransactionOrder AS Summation
WHERE Summation.TransactionOrderNum<= #TransactionOrder.TransactionOrderNum
AND Summation.CustId = #TransactionOrder.CustId
GROUP BY CustId
) AS SumValues
这绝对应该可以为您提供最终金额:
SELECT tbl_Customer.CustId, tbl_Customer.Name, SUM
(
CASE WHEN TransactionOrderNum = 1 THEN DtAmt - CrAmt
ELSE (CrAmt * -1) END
) AS CustomerSum
FROM tbl_Customer
LEFT JOIN #TransactionOrder
ON tbl_Customer.CustId = #TransactionOrder.CustId
GROUP BY tbl_Customer.CustId, tbl_Customer.Name
您可以使用CTE,也可以:
WITH TransactionOrder AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CustId ORDER BY TransDate)
AS TransactionOrderNum
FROM tbl_Transaction
)
SELECT TransactionOrder.*, CustomerSum AS Balance
FROM TransactionOrder
CROSS APPLY
(
SELECT SUM(
CASE WHEN TransactionOrderNum = 1 THEN DtAmt - CrAmt
ELSE (CrAmt * -1) END
) AS CustomerSum
FROM TransactionOrder AS Summation
WHERE Summation.TransactionOrderNum<= TransactionOrder.TransactionOrderNum
AND Summation.CustId = TransactionOrder.CustId
GROUP BY CustId
) AS SumValues
现在,如果这是一个数据流,那么您可以将临时选择存储到临时表中,并为每个custid
选择max(TransactionOrderNum)行。在余额随需查询中的select
和from
之间添加此内容
INTO #BalanceAsYouGo
然后最终查询变得更像这样:
SELECT tbl_Customer.CustId, tbl_Customer.Name, #BalanceAsYouGo.Balance
FROM tbl_Customer
LEFT JOIN #BalanceAsYouGo
ON tbl_Customer.CustId = #BalanceAsYouGo.CustId
LEFT JOIN
(
SELECT CustId, MAX(TransactionOrderNum) AS MaxTransNum
FROM #BalanceAsYouGo
GROUP BY CustId
) AS FinalBalance
ON FinalBalance.CustId = #BalanceAsYouGo.CustId
FinalBalance.MaxTransNum = #BalanceAsYouGo.TransactionOrderNum
GROUP BY tbl_Customer.CustId, tbl_Customer.Name
答案 1 :(得分:1)
我使用递归CTE计算运行余额,然后使用外部应用来获得最终结果。
我从您的示例输出中了解到,当用户的余额达到0时,您开始用借方 - 贷方计算余额,而不是继续公式之前的余额 - 贷记。这就是CTE完成的目标。
WITH rk AS (SELECT ROW_NUMBER() OVER (PARTITION BY custid ORDER BY transid) rn, transid, custid, tdate, dtamt, cramt
FROM tbl_transaction)
,bl AS (SELECT rk.rn, rk.transid, rk.custid, rk.tdate, rk.dtamt, rk.cramt, rk.dtamt - rk.cramt balance
FROM rk
WHERE rk.rn = 1
UNION ALL
SELECT rk2.rn, rk2.transid, rk2.custid, rk2.tdate, rk2.dtamt, rk2.cramt
,CASE WHEN bl.balance = 0 THEN rk2.dtamt - rk2.cramt ELSE bl.balance - rk2.cramt END balance
FROM bl, rk rk2
WHERE bl.rn = rk2.rn - 1 and bl.custid = rk2.custid)
-- SELECT * FROM bl ORDER BY custid, transid
SELECT cc.custid, cc.NAME, xx.Balance
FROM tbl_Customer cc
OUTER APPLY (SELECT TOP 1 bl.Balance
FROM bl
WHERE bl.custid = cc.custid
ORDER BY bl.rn DESC) xx
CTE中未注释的选择返回定义的输出。您可以在CTE之后切换注释,以查看CTE完全按照您的定义返回中间表。 (截至2:35P)
当然,加法和减法是可交换的。真的,最终的平衡可以回答:
SELECT cc.custid, cc.NAME, xx.Balance
FROM tbl_Customer cc
OUTER APPLY (SELECT SUM(tt.dtamt) - SUM(tt.cramt) Balance
FROM tbl_Transaction tt
WHERE tt.custid = cc.custid) xx
这种查询必然会长期运行。您可以尝试在tbl_Transaction:
中的聚合上创建索引视图CREATE VIEW dbo.dv_TransactionTotals
WITH SCHEMABINDING
AS
SELECT tt.custid, SUM(tt.dtamt) - SUM(tt.cramt) Balance, COUNT_BIG(*) Transactions
FROM dbo.tbl_Transaction tt -- assuming schema here
GROUP BY tt.custid
CREATE CLUSTERED INDEX ixc_TransactionTotals ON dbo.dv_TransactionTotals (custid)
预计此索引需要一段时间才能构建,并意识到它的存在会对您的插入产生一些影响。有关详细信息,请参阅http://technet.microsoft.com/en-us/library/cc917715.aspx。 (注意索引需要COUNT_BIG字段 - 我总是忘记这一点,直到我尝试运行索引。还要注意表格必须由模式正确限定;我假设dbo没有提供其他。)
SQL 2005中的聚合感知可能会导致它自动使用视图,但我从不相信它。您也可以将查询转换为具有事务和没有事务的那些的联合:
SELECT cc.custid, cc.NAME, tt.Balance
FROM tbl_Customer cc
INNER JOIN dv_TransactionTotals tt ON tt.custid = cc.custid
UNION ALL
SELECT cc.custid, cc.NAME, NULL Balance
FROM tbl_Customer cc
WHERE cc.custid NOT IN (SELECT custid FROM dv_TransactionTotals tt WHERE tt.custid = cc.custid)
我希望你不会在生产中运行这个怪物,因为这显然是一个报道问题。实际上,如果我遇到这个问题,我会创建(或利用)数据仓库,在必要的表上设置复制,并创建一个定期将这些数据添加到事实表的过程。