我正在尝试在SQL中实现一个算法(transact sql)并且鉴于我目前的能力很难找到它。我试图将问题排除在问题之外。这个算法背后的基本思想是用户计划一个月的预算。他们很清楚金钱来来往往多少钱。现在是月中。问题是:根据当前的义务,在本月的剩余时间里,账户的最差位置是什么?
例如,查看下面的时间线,让我们说
Today = 15th
Util = 17th
B-day = 19th
Cable = 22nd
Wages = 25th
17日帐户将比今天少150美元。 在19号,帐户将比今天多100美元。 在22日,该帐户将比今天少25美元。 在25日,该帐户将比今天多975美元。
因此,在此示例中,查询将返回 - $ 150。
注意:我只关心返回的负值。如果它是否定的,则表示您有义务,不应该花费该金额。如果是积极的,那也没关系。您不能在帐户中花钱。
| | |
| ^ ^ | ^ ^ |
| |Rent(-500) |Phone(-50) | |Util(-150) |Cable(-125) |
-----------------------------------------------------------------------------------
| ^ | ^ ^ |
| |Wages(+1000) | |B-day(+250) |Wages(+1000) |
| | |
Past Today Future
我们可以用于此问题的简单表:
create table MoneyFlow
(
fiscalEventID int not null,
value money,
transactionDate date
)
另一种看待它的方法。你如何在SQL中执行以下算法?
Algorithm
Input: Start date, End date
Output: Worst position the account is going to be in in the future.
WorstPosition = 0 //only want worst position if it is negative.
For each date D between start date and end date where a transaction takes place
Position_D = Sum deposits and withdrawls between start date and D
If Position_D < WorstPosition
WorstPosition = Position_D
return WorstPosition
还有一点请注意我使用的数据库是Sybase
如果您需要澄清任何细节,请与我们联系。谢谢!
答案 0 :(得分:5)
在我看来,您正在尝试创建一个运行总计,然后从运行总计中选择最小的运行值。
以下内容并不漂亮,但它避免使用游标。
从以下内容开始填充表格:
CREATE TABLE #temp
(someDate datetime
,amount decimal)
INSERT INTO #temp (someDate, amount)
SELECT '2009-01-01', 1000 UNION ALL
SELECT '2009-01-02', -500 UNION ALL
SELECT '2009-01-03', -50 UNION ALL
SELECT '2009-01-04', -150 UNION ALL
SELECT '2009-01-05', 250 UNION ALL
SELECT '2009-01-06', -125 UNION ALL
SELECT '2009-01-07', 1000
这是一个简单的查询,可以获得最小的运行总数:
SELECT
TOP 1
base.someDate
,runningTotal =
(SELECT sum(derived.amount)
FROM #temp derived
WHERE derived.someDate <= base.someDate)
FROM #temp base
ORDER BY runningTotal ASC
答案 1 :(得分:3)
我不确定Sybases T-SQL,但MS SQL的方言你可以使用如下的技巧。
请注意虽然它有效但我不确定它是否有记录的行为。要确定你应该使用像psasik建议的光标。
SET NOCOUNT ON
CREATE TABLE MoneyFlow
(
fiscalEventID INT NOT NULL,
value MONEY,
transactionDate DATETIME
)
go
INSERT INTO MoneyFlow VALUES(1, 1000, '2009-08-25')
INSERT INTO MoneyFlow VALUES(1, -500, '2009-08-30')
INSERT INTO MoneyFlow VALUES(1, -50, '2009-09-01')
-- Today
INSERT INTO MoneyFlow VALUES(1, -150, '2009-09-17') -- -150
INSERT INTO MoneyFlow VALUES(1, +250, '2009-09-19') -- +100
INSERT INTO MoneyFlow VALUES(1, -125, '2009-09-22') -- -25
INSERT INTO MoneyFlow VALUES(1, 1000, '2009-09-25') -- +975
--INSERT INTO MoneyFlow VALUES(1, -2000, '2009-09-25') -- -1025
GO
DECLARE @curr MONEY
, @min MONEY
SELECT @curr = 0
, @min = 0
SELECT @curr = @curr + value
, @min = CASE
WHEN @curr < @min THEN @curr
ELSE @min
END
FROM MoneyFlow f (NOLOCK)
WHERE f.transactionDate > '2009-09-15'
SELECT @min
GO
DROP TABLE MoneyFlow
答案 2 :(得分:1)
这是MS TSQL,但我想它在sybase中是类似的
SELECT MIN(lmv.value)
FROM @moneyFlow mv
JOIN (
SELECT SUM(mv.value) as [VALUE], lmv.fiscalEventID
FROM @moneyFlow mv
JOIN @moneyFlow lmv ON mv.transactionDate <= lmv.transactionDate
WHERE mv.transactionDate >= @Start AND mv.transactionDate <= @End
AND lmv.transactionDate >= @Start AND lmv.transactionDate <= @End
GROUP BY lmv.fiscalEventID
) lmv ON mv.fiscalEventID = lmv.fiscalEventID
WHERE lmv.value < 0
DECLARE @Start DATETIME
SET @Start = '1/2/09'
DECLARE @End DATETIME
SET @End = '1/6/09'
DECLARE @moneyFlow TABLE (
fiscalEventID int not null,
value money,
transactionDate DATETIME
)
INSERT @moneyFlow VALUES (1, 1000, '1/1/09')
INSERT @moneyFlow VALUES (2, -500, '1/2/09')
INSERT @moneyFlow VALUES (3, -50, '1/3/09')
INSERT @moneyFlow VALUES (4, -150, '1/4/09')
INSERT @moneyFlow VALUES (5, 250, '1/5/09')
INSERT @moneyFlow VALUES (6, -125, '1/6/09')
INSERT @moneyFlow VALUES (7, 1000, '1/7/09')
答案 3 :(得分:0)
冒着被告知我没有回答这个问题的风险,为什么有必要在SQL中完成?似乎业务逻辑可能更适合应用程序层,甚至可能在其他地方重用。
答案 4 :(得分:0)
我不熟悉Sybase,所以我不知道你是否可以这样做,但我会尝试以下内容:
select a.transactionDate as balanceDate
, (select sum(value)
from MoneyFlow b
where b.transactionDate <= a.transactionDate
) as balance
from MoneyFlow a
order by 2
这应该会告诉你平衡最低点的那一天。您可能需要调整此开始日期和起始余额。同样,如果您只想返回一天,则需要将输出限制在第一行。
答案 5 :(得分:0)
此查询提供了运行总计:
Select M1.TransactionDate, Sum(M2.Money)
From MoneyFlow M1
Join MoneyFlow M2
On M2.TransactionDate <= M1.TransactionDate
Group By M1.TransactionDate
你想要最小的这些,所以这个SQL应该这样做..
Select Min(RunBalance) From
(Select M1.TransactionDate, Sum(M2.Money) RunBalance
From MoneyFlow M1
Join MoneyFlow M2
On M2.TransactionDate <= M1.TransactionDate
Group By M1.TransactionDate) Z
要将输出限制为负值,请添加预设值 (但是SQL应该只生成一行,所以如果没有负数,这将导致空输出...)
Select Min(RunBalance) From
(Select M1.TransactionDate, Sum(M2.Money) RunBalance
From MoneyFlow M1
Join MoneyFlow M2
On M2.TransactionDate <= M1.TransactionDate
Group By M1.TransactionDate) Z
Where RunBalance > 0
答案 6 :(得分:0)
select min(Value)
from (
select sum(value) as Value
from MoneyFlow
group by transactionDate
where transactionDate between @startdate and @enddate
) a
where min(Value) < 0