我有一张借记表和一张信用表。我需要尽可能地尝试匹配它们。
所以这是我的想法 - 但我对编码逻辑并不是最好的......如果有人有更好的想法,请随意加入。
Table 1 - Debits
$1.50
$1.33
$2.55
$2.66
Table 2 - Credits
$5.00
$3.55
$1.55
$1.35
$1.33
所以这是我的思考过程:
所以在我上面的代码示例的情况下......借记将采用以下信用
$1.50 -> $1.55
$1.33 -> $1.33
$2.55 -> $3.55
$2.66 -> $5.00
如果有人可以帮助构建代码逻辑,那将非常感激。除非我发现这一切都错了,否则应该尝试MS Access可以做的其他事情......
答案 0 :(得分:3)
这听起来像是装箱问题。根据记录的数量和您需要解决的速度,它将决定您可以使用的算法。对于较少数量的记录,您可以强制它以找到最佳解决方案。如果它非常大,那么基于启发式算法会更好。
基本上,具体的实现方式取决于可以制作多少条记录组合。 (m学分和n学分表示m * n组合。我想你可以通过删除完全匹配来轻松开始优化这一点)
http://en.wikipedia.org/wiki/Bin_packing_problem
修改
第一次适合下降的结果。好消息是,如果适合,您可以将多个借记合并为一个借方。现在,如果您不想要这种行为,它很容易修改。您只需检查是否使用了空间,而不是检查是否有足够的空间。 您可以使用其他启发式方法,最佳拟合,最差拟合等。在这种情况下,我个人最有可能首先尝试分配所有完全匹配,然后在剩余部分上运行此算法,但正如我在之前的评论中提到的那样,这是一种取决于数据集大小的情况/解决方案。
$5.00 -> $2.66, $1.50
$3.55 -> $2.55
$1.55 -> $1.33
Public Type Record
ID As Long
Amount As Currency
Allocated As Boolean
AllocatedAmount As Currency
End Type
Public Function PaymentAllocation()
Dim cr() As Record
Dim de() As Record
' function returns records with amounts in descending order
cr = QueryToArray("CR", "CR_AMT")
de = QueryToArray("DE", "DE_AMT")
' first fit descending order
' starting with largest credit and debit, check if they fit
' if fit, then allocate
' if not, then check next credit
Dim i As Long, j As Long
For i = LBound(cr) To UBound(cr)
For j = LBound(de) To UBound(de)
If de(j).Allocated = False Then
If (cr(i).Amount - cr(i).AllocatedAmount) >= de(j).Amount Then
de(j).Allocated = True
cr(i).AllocatedAmount = cr(i).AllocatedAmount + de(j).Amount
Debug.Print cr(i).Amount, de(j).Amount
End If
End If
Next
Next
End Function
Public Function QueryToArray(tableName, fieldName) As Record()
Dim rs As Recordset
Dim ans() As Record
Dim i As Long
Set rs = CurrentDb.OpenRecordset("SELECT * FROM " & tableName & " ORDER BY " & tableName & "_AMT DESC;", dbOpenDynaset)
ReDim ans(1 To DCount(tableName & "_ID", tableName))
i = 0
Do Until rs.EOF
i = i + 1
ans(i).ID = rs(tableName & "_ID").Value
ans(i).Amount = rs(fieldName).Value
ans(i).Allocated = False
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
QueryToArray = ans
End Function
答案 1 :(得分:0)
tblDebits
id amount credit_id
1 $1.50
2 $1.33
3 $2.55
4 $2.66
tblCredits
id amount
1 $5.00
2 $3.55
3 $1.55
4 $1.35
5 $1.33
然后从按钮或其他东西运行以下功能:
Function AssignCredits()
Dim db As Database
Dim rs1 As DAO.Recordset
Dim rs2 As DAO.Recordset
Dim blnCreditAssigned As Boolean
Set db = CurrentDb
Set rs1 = db.OpenRecordset("SELECT tblDebits.id, tblDebits.amount, tblDebits.credit_id FROM tblDebits ORDER BY tblDebits.amount ASC")
Set rs2 = db.OpenRecordset("SELECT tblCredits.id, tblCredits.amount FROM tblCredits ORDER BY tblCredits.amount ASC")
rs1.MoveFirst
rs2.MoveFirst
Do Until rs1.EOF
Do Until blnCreditAssigned = True Or rs2.EOF
If rs2![amount] >= rs1![amount] Then
rs1.Edit
rs1![credit_id] = rs2![id]
rs1.Update
blnCreditAssigned = True
End If
rs2.MoveNext
Loop
rs1.MoveNext
blnCreditAssigned = False
Loop
rs1.Close
rs2.Close
MsgBox "Credit assignment complete"
End Function
答案 2 :(得分:0)
好的,既然你说你必须将信用证与借记相匹配,我认为信用证只能使用一次。我认为你找到这个代码来完成这项工作。
Sub CreditMatch()
Dim rsDebit As Recordset
Dim rsCredit As Recordset
Dim strSQL As String
strSQL = "SELECT * FROM tblDebits ORDER BY Amount"
Set rsDebit = CurrentDb.OpenRecordset(strSQL)
Do
strSQL = "SELECT * "
strSQL = strSQL & "FROM tblCredits "
strSQL = strSQL & "WHERE ID Not In (SELECT CreditID FROM tblDebits) AND Amount >=" & rsDebit("Amount")
strSQL = strSQL & " ORDER BY Amount"
Set rsCredit = CurrentDb.OpenRecordset(strSQL)
If Not rsCredit.EOF Then
rsDebit.Edit
rsDebit("CreditID") = rsCredit("ID")
rsDebit.Update
End If
rsDebit.MoveNext
Loop Until rsDebit.EOF
End Sub
答案 3 :(得分:0)
这仍然需要更多的工作,但它提供了你需要的答案:
select debit_value, min(credit_value) as match_credit_value
from
(
select d.value as debit_value, c.value as credit_value, d.id as did, c.id as cid
from debits d
cross join credits c
where c.value >= d.value and d.id < c.id
) x
group by x.debit_value
+-------------+--------------------+
| debit_value | match_credit_value |
+-------------+--------------------+
| 1.33 | 1.35 |
| 1.5 | 1.55 |
| 2.55 | 3.55 |
| 2.66 | 5 |
+-------------+--------------------+
使用此SQL脚本生成的数据:
create table debits ( id integer, value money );
insert into debits values (1, 1.33);
insert into debits values (2, 1.50);
insert into debits values (3, 2.55);
insert into debits values (4, 2.66);
create table credits ( id integer, value money );
insert into credits values (1, 1.33);
insert into credits values (2, 1.35);
insert into credits values (3, 1.55);
insert into credits values (4, 3.55);
insert into credits values (5, 5.00);
答案 4 :(得分:0)
因为您似乎正在尝试通过将借记与贷记相匹配来计算帐户余额。这是另一种方法。最后,您将获得借记卡关闭的信息(是复数)。如果这不是......你正在寻找请告诉。
您有多个与多个借记/点数匹配的点数/借记,例如:
debits - credits
$1.50 -> $0.00 - ($5.00) $5.00 -> $3.50
$1.33 -> $0.00 - ($5.00) $3.50 -> $2.17
$2.55 -> $0.38 - ($5.00) $2.17 -> $0.00
($2.55) $0.38 -> $0.00 - ($3.55) $3.55 -> $3.17
$2.66 -> $0.00 - ($3.55) $3.17 -> $0.51
no more debits thus account has credit balance:
$0.51 + $1.55 + $1.35 + $1.33
答案 5 :(得分:0)
仍然不确定你在做什么或为什么(你需要日期和客户ID,对吧?)但是如果你想要做的是将信用与差额最小化的借方相匹配,那么包括差额(信用卡借记&gt; ; = 0)在您的信用和借记之间的连接中(在customerID上,可能受creditDate&gt; = debitDate限制),然后将信用应用于序列中的差额,并且差异递减。
我假设您有另一张桌子,因为信用卡可以申请多笔借记卡,或者借记卡可以通过多个信用卡支付,但您没有提及。无论如何,如果我这样做(我明白你要做什么),我会先加入信用卡和借记金额,这样就可以先进行完全匹配。然后,您可以从信用卡和借记行的迭代检查中排除这些。