PAYATHON 2026

Access SQL 查询每笔交易的上一次付款日期

支付小周

结论

可以使用相关子查询,查找同一 TenantID 下、日期早于当前交易且 TransactionType = 2 的记录,再用 Max() 取得最近一次付款日期。如果没有符合条件的付款记录,Max() 会返回 Null,当前交易仍会保留。

假设现有查询名为 qryTransactions:

SELECT
    T.TransID,
    T.TenantID,
    T.TransactionType,
    T.Amount,
    T.TransactionDate,
    (
        SELECT Max(P.TransactionDate)
        FROM qryTransactions AS P
        WHERE P.TenantID = T.TenantID
          AND P.TransactionType = 2
          AND P.TransactionDate < T.TransactionDate
    ) AS PreviousPaymentDate
FROM qryTransactions AS T
ORDER BY
    T.TenantID,
    T.TransactionDate,
    T.TransID;

查询原理

相关子查询会针对外层查询中的每笔交易执行一次:

  1. P.TenantID = T.TenantID:只查找当前客户的交易。
  2. P.TransactionType = 2:只考虑付款记录。
  3. P.TransactionDate < T.TransactionDate:只考虑当前交易之前发生的付款。
  4. Max(P.TransactionDate):从符合条件的付款记录中取得最近的日期。

外层查询没有使用 INNER JOIN,所以找不到历史付款时也不会过滤当前记录,PreviousPaymentDate 会返回 Null。

使用表名时的写法

如果数据直接来自名为 Transactions 的表,可以写成:

SELECT
    T.TransID,
    T.TenantID,
    T.TransactionType,
    T.Amount,
    T.TransactionDate,
    (
        SELECT Max(P.TransactionDate)
        FROM Transactions AS P
        WHERE P.TenantID = T.TenantID
          AND P.TransactionType = 2
          AND P.TransactionDate < T.TransactionDate
    ) AS PreviousPaymentDate
FROM Transactions AS T
ORDER BY
    T.TenantID,
    T.TransactionDate,
    T.TransID;

如果表名或字段名与 Access 保留字冲突,建议加上方括号:

SELECT
    T.[TransID],
    T.[TenantID],
    T.[TransactionType],
    T.[Amount],
    T.[TransactionDate],
    (
        SELECT Max(P.[TransactionDate])
        FROM [Transactions] AS P
        WHERE P.[TenantID] = T.[TenantID]
          AND P.[TransactionType] = 2
          AND P.[TransactionDate] < T.[TransactionDate]
    ) AS [PreviousPaymentDate]
FROM [Transactions] AS T;

注意事项

同一时间发生多笔交易

上述查询将“之前”定义为:

P.TransactionDate < T.TransactionDate

因此,与当前交易具有相同 TransactionDate 的付款不会被视为上一笔付款。这样处理通常更安全,因为仅凭相同的时间无法判断记录的先后顺序。

如果业务规则规定时间相同时,TransID 较小的记录先发生,就需要同时根据 TransactionDate 和 TransID 判断顺序。不过,如果返回值仍然只有日期,同一时间下的结果看起来会与当前交易日期相同。更稳妥的做法是让 TransactionDate 保存足够精确的时间,或者增加明确的交易顺序字段。

Null 交易日期

如果当前记录的 TransactionDate 为 Null,以下比较条件不会匹配任何记录:

P.TransactionDate < T.TransactionDate

因此,PreviousPaymentDate 也会是 Null。

查询性能

相关子查询会为每条外层记录查找历史付款。数据量较大时,建议为以下字段建立索引:

  • TenantID
  • TransactionType
  • TransactionDate

如果 Access 支持,并且数据规模确实需要进一步优化,可以考虑为这些字段建立复合索引。复合索引的实际效果取决于数据分布和查询计划,应使用真实数据进行测试。

备注:内容仅供参考。