In addition to the simple (=boring) solution with a cursor, you can probably do this by creating an aggregate function (or rather, 2 aggregate functions, one to calculate balance 1 and one to calculate balance 2). The trouble is that you can only use one argument to an aggregate function, so that argument would have to be a composite type. In pseudo code (I have not used Oracle for so long):
CREATE TYPE tuple_type(amt number, bal1 number, bal2 number);
CREATE FUNCTION calc_bal1(arg IN tuple_type) RETURN number AGGREGATE USING some_implementing_type;
CREATE FUNCTION calc_bal2(arg IN tuple_type) RETURN number AGGREGATE USING some_implementing_type;
Then you can query them with analytic functions. If you are only interested in the final value for each account, you can do:
SELECT t1.acct_no,
calc_bal1(tuple_type(t2.amt, t1.bal1, t1.bal2)) OVER (PARTITION BY t1.acct_no ORDER BY t2.datenum),
calc_bal2(tuple_type(t2.amt, t1.bal1, t1.bal2)) OVER (PARTITION BY t1.acct_no ORDER BY t2.datenum)
FROM table1 t1
JOIN (SELECT acct_no, datenum, amt FROM table2
UNION ALL
SELECT acct_no, 0, 0) t2
ON t1.acct_no = t2.acct_no;
WHERE t1.datenum = 0;
If you want every singe transction, do:
SELECT t1.acct_no,
calc_bal1(tuple_type(t2.amt, t1.bal1, t1.bal2))
OVER (PARTITION BY t1.acct_no
ORDER BY t2.datenum
ROWS BETWEEN UNBOUNDED PRECEEDING AND CURRENT ROW),
calc_bal1(tuple_type(t2.amt, t1.bal1, t1.bal2))
OVER (PARTITION BY t1.acct_no
ORDER BY t2.datenum
ROWS BETWEEN UNBOUNDED PRECEEDING AND CURRENT ROW)
FROM table1 t1
JOIN (SELECT acct_no, datenum, amt FROM table2
UNION ALL
SELECT acct_no, 0, 0) t2
ON t1.acct_no = t2.acct_no;
You can also do it with cursors instead of aggregates (which is very likely to have terrible performance):
CREATE FUNCTION calc_bal1(c IN sys.ref_cursor, bal1 IN number, bal2 IN number) RETURN number AS ...;
CREATE FUNCTION calc_bal2(c IN sys.ref_cursor, bal1 IN number, bal2 IN number) RETURN number AS ...;
If you want all rows:
SELECT t1.acct_no,
calc_bal1(CURSOR(SELECT amt FROM table2 x WHERE x.acct_no = t1.acct_no AND x.datenum <= t2.datenum ORDER BY x.datenum), t1.bal1, t1.bal2),
calc_bal2(CURSOR(SELECT amt FROM table2 x WHERE t2.acct_no = t1.acct_no AND x.datenum <= t2.datenum ORDER BY t2.datenum), t1.bal1, t1.bal2)
FROM table1 t1
JOIN (SELECT acct_no, datenum, amt FROM table2
UNION ALL
SELECT acct_no, 0, 0) t2
ON t1.acct_no = t2.acct_no;
If you only want the final values:
SELECT t1.acct_no,
calc_bal1(CURSOR(SELECT amt FROM table2 t2 WHERE t2.acct_no = t1.acct_no ORDER BY t2.datenum), t1.bal1, t1.bal2),
calc_bal2(CURSOR(SELECT amt FROM table2 t2 WHERE t2.acct_no = t1.acct_no ORDER BY t2.datenum), t1.bal1, t1.bal2)
FROM table1 t1;