数据库记录集匹配美元金额

时间:2014-08-18 17:44:09

标签: sql ms-access

我有一张借记表和一张信用表。我需要尽可能地尝试匹配它们。

所以这是我的想法 - 但我对编码逻辑并不是最好的......如果有人有更好的想法,请随意加入。

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. 打开信用记录集并按信用额度排序DESCENDING。
  2. 打开借方的记录集,并按借记金额对ASCENDING进行排序。
  3. 逐个浏览借记记录集 - 因为对于每笔借记我们需要匹配等于或大于借方金额的贷项
  4. 逐个浏览信用记录集 - 这就是我们如何获得等于或大于
  5. 的信用额度
  6. 如果借方金额等于贷方金额,请停止并标记该借方的贷方金额。如果借方金额达到的借贷金额大于借记金额,则停止并取出大于该金额的第一个金额。
  7. 所以在我上面的代码示例的情况下......借记将采用以下信用

    $1.50 -> $1.55
    $1.33 -> $1.33
    $2.55 -> $3.55
    $2.66 -> $5.00
    

    如果有人可以帮助构建代码逻辑,那将非常感激。除非我发现这一切都错了,否则应该尝试MS Access可以做的其他事情......

6 个答案:

答案 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)

因为您似乎正在尝试通过将借记与贷记相匹配来计算帐户余额。这是另一种方法。最后,您将获得借记卡关闭的信息(是复数)。如果这不是......你正在寻找请告诉。

  1. 借一笔借记$ 1.50
  2. 一笔5美元(订单无关紧要)
  3. if(debit&gt; = credit)debit - credit - =&gt; debitRemainder
    if(借记&lt; credit)credit - debit - =&gt; creditRemainder
  4. 如果debitRemainder> 0,则将其作为新借记并转到步骤2
  5. 如果creditRemainder> 0,则将其作为新的赠送金额并转到第1步(跳过第2步,而您已有信用额)
  6. 您有多个与多个借记/点数匹配的点数/借记,例如:

    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限制),然后将信用应用于序列中的差额,并且差异递减。

我假设您有另一张桌子,因为信用卡可以申请多笔借记卡,或者借记卡可以通过多个信用卡支付,但您没有提及。无论如何,如果我这样做(我明白你要做什么),我会先加入信用卡和借记金额,这样就可以先进行完全匹配。然后,您可以从信用卡和借记行的迭代检查中排除这些。