【问题标题】:Find the latest payment value in between "valid" period查找“有效”期间之间的最新付款值
【发布时间】:2017-10-26 15:57:00
【问题描述】:

我很难写出好标题,所以我将尝试在这里详细描述问题。

我们有表 [dbo].[LEASE_APPLICATIONS_AUDIT_LOG] 存储更改日期时间的历史数据。如果数据的状态不是 IN ('E', 'F', 'I', 'O', 'X') 那么这意味着它在那个时候被批准了并且如果状态不是这些的,那么它不被批准。它可以按任何顺序多次获得批准和不批准。例如:

'2010.01.01', 'A'
'2010.02.01', 'B'
'2010.03.01', 'E'
'2010.04.01', 'Z'

也就是说,该记录是从2010.01.01到2010.03.01批准的,然后在2010.04.01再次批准。

还有另一个表 [dbo].[LEASE_FINANCING_AUDIT_LOG] 存储当时的基本支付金额。例如,如果对于同一条记录,我会有这样的条目:

'2010.01.01', 123
'2010.04.01', 321

这意味着从 2010.01.01 到 2010.03.01(未获批准)的基本付款为 123,然后从 2010.04.01 开始,该值变为 321。

状态变化可能有多种组合,并且在任何时间段都可能有不同的基本付款值。

因此,我们的目标是找到在 APPROVED 期间之间的 LATEST base_payment 值。

这是我们目前制作的脚本。有 2 个包含该数据和函数的表。其他代码 sn-ps 取自单元测试,这就是为什么它们会这样,如果逻辑不符合预期,它们将输出“坏”字符串。该功能正在运行,但我不太喜欢 TOP 1/ORDER BY 方法,并试图找出是否有更好的方法来实现它。有什么想法吗?

SET NOCOUNT ON;

CREATE TABLE [dbo].[LEASE_APPLICATIONS_AUDIT_LOG]
(
    [LEASE_APPLICATION]       CHAR(8)
  , [APPLICATION_STATUS_CODE] CHAR(1)
  , [CHANGED_DATE]            DATETIME2(7) NOT NULL
);
GO

CREATE TABLE [dbo].[LEASE_FINANCING_AUDIT_LOG]
(
    [LEASE_APPLICATION] CHAR(8)
  , [BASE_PAYMENT]      DECIMAL(10, 2) NULL
  , [CHANGED_DATE]      DATETIME2(7)   NOT NULL
);
GO

CREATE FUNCTION [dbo].[post_approval_payment_amount] (@lease_application CHAR(8))
RETURNS TABLE
AS
RETURN (
           SELECT TOP 1 lfal.BASE_PAYMENT AS post_approval_payment_amount
             FROM LEASE_APPLICATIONS_AUDIT_LOG laal
            CROSS APPLY
                (
                    SELECT TOP 1 lf.BASE_PAYMENT
                      FROM LEASE_FINANCING_AUDIT_LOG lf
                     WHERE lf.LEASE_APPLICATION = laal.LEASE_APPLICATION
                       AND lf.CHANGED_DATE      < COALESCE((
                                                               SELECT TOP 1 la.CHANGED_DATE
                                                                 FROM LEASE_APPLICATIONS_AUDIT_LOG la
                                                                WHERE la.LEASE_APPLICATION = laal.LEASE_APPLICATION
                                                                  AND la.CHANGED_DATE      > laal.CHANGED_DATE
                                                                ORDER BY la.CHANGED_DATE
                                                           ), CAST('9999-12-31 23:59:59' AS DATETIME))
                     ORDER BY lf.CHANGED_DATE DESC
                )                              lfal
            WHERE laal.LEASE_APPLICATION = @lease_application
              AND laal.APPLICATION_STATUS_CODE NOT IN ('E', 'F', 'I', 'O', 'X')
            ORDER BY laal.CHANGED_DATE DESC
       );
GO

DECLARE @lease_application CHAR(8) = '35163328'
      , @base_payment      DECIMAL = 209.12
      , @expected          DECIMAL = 209.12
      , @actual            DECIMAL;

DECLARE @la AS TABLE
(
    change_date             DATETIME2(7)
  , application_status_code CHAR(1)     NULL
  , base_amount             DECIMAL     NULL
  , is_laal                 BIT
);

INSERT INTO @la (   change_date
                  , application_status_code
                  , base_amount
                  , is_laal
                )
VALUES ('2017-05-11 03:46:26.4800000', 'K', NULL, 1)
     , ('2017-05-11 03:48:05.0600000', NULL, @base_payment, 0)
     , ('2017-06-21 14:07:51.2200000', 'X', NULL, 1);

INSERT INTO dbo.lease_applications_audit_log (   LEASE_APPLICATION
                                               , CHANGED_DATE
                                               , APPLICATION_STATUS_CODE
                                             )
SELECT @lease_application
     , l.change_date
     , l.application_status_code
  FROM @la AS l
 WHERE l.is_laal = 1;

INSERT INTO dbo.lease_financing_audit_log (   LEASE_APPLICATION
                                            , CHANGED_DATE
                                            , BASE_PAYMENT
                                          )
SELECT @lease_application
     , l.change_date
     , l.base_amount
  FROM @la AS l
 WHERE l.is_laal = 0;

SELECT @actual = post_approval_payment_amount
  FROM [dbo].[post_approval_payment_amount](@lease_application);

IF (@expected <> @actual) SELECT 'Test 1 failed'
ELSE SELECT 'Test 1 passed';


SELECT @lease_application = '30000152'
     , @base_payment      = 622.15
     , @expected          = 622.15;

DELETE FROM @la;

INSERT INTO @la (   change_date
                  , application_status_code
                  , base_amount
                  , is_laal
                )
VALUES ('2017-05-11 03:46:26.4800000', 'z', NULL, 1)
     , ('2017-05-11 03:48:05.0600000', NULL, @base_payment, 0);

INSERT INTO dbo.lease_applications_audit_log (   LEASE_APPLICATION
                                               , CHANGED_DATE
                                               , APPLICATION_STATUS_CODE
                                             )
SELECT @lease_application
     , l.change_date
     , l.application_status_code
  FROM @la AS l
 WHERE l.is_laal = 1;

INSERT INTO dbo.lease_financing_audit_log (   LEASE_APPLICATION
                                            , CHANGED_DATE
                                            , BASE_PAYMENT
                                          )
SELECT @lease_application
     , l.change_date
     , l.base_amount
  FROM @la AS l
 WHERE l.is_laal = 0;

SELECT @actual = post_approval_payment_amount
  FROM [dbo].[post_approval_payment_amount](@lease_application);

IF (@expected <> @actual) SELECT 'Test 2 failed'
ELSE SELECT 'Test 2 passed';

SELECT @lease_application = '38768578'
     , @base_payment      = 453.70
     , @expected          = NULL
     , @actual            = NULL;

DELETE FROM @la;

INSERT INTO @la (   change_date
                  , application_status_code
                  , base_amount
                  , is_laal
                )
VALUES ('2017-05-11 03:46:26.4800000', 'L', NULL, 1)
     , ('2017-06-09 12:00:36.2000000', 'X', NULL, 1)
     , ('2017-06-12 03:48:05.0600000', NULL, @base_payment, 0);

INSERT INTO dbo.lease_applications_audit_log (   LEASE_APPLICATION
                                               , CHANGED_DATE
                                               , APPLICATION_STATUS_CODE
                                             )
SELECT @lease_application
     , l.change_date
     , l.application_status_code
  FROM @la AS l
 WHERE l.is_laal = 1;

INSERT INTO dbo.lease_financing_audit_log (   LEASE_APPLICATION
                                            , CHANGED_DATE
                                            , BASE_PAYMENT
                                          )
SELECT @lease_application
     , l.change_date
     , l.base_amount
  FROM @la AS l
 WHERE l.is_laal = 0;

SELECT @actual = post_approval_payment_amount
  FROM [dbo].[post_approval_payment_amount](@lease_application);

IF (@actual IS NOT NULL) SELECT 'Test 3 failed'
ELSE SELECT 'Test 3 passed';

SELECT @lease_application = '38282661'
     , @base_payment      = 451.25
     , @expected          = 451.25;

DELETE FROM @la;

INSERT INTO @la (   change_date
                  , application_status_code
                  , base_amount
                  , is_laal
                )
VALUES ('2017-05-11 03:46:26.4800000', 'O', NULL, 1)
     , ('2017-05-11 03:48:05.0600000', NULL, @base_payment, 0)
     , ('2017-07-05 10:52:14.6800000', 'O', NULL, 1)
     , ('2017-07-05 11:10:24.0400000', 'E', NULL, 1)
     , ('2017-07-05 11:10:25.6000000', 'E', NULL, 1)
     , ('2017-07-05 11:10:49.1900000', 'L', NULL, 1)
     , ('2017-07-06 00:04:30.6400000', 'O', NULL, 1);

INSERT INTO dbo.lease_applications_audit_log (   LEASE_APPLICATION
                                               , CHANGED_DATE
                                               , APPLICATION_STATUS_CODE
                                             )
SELECT @lease_application
     , l.change_date
     , l.application_status_code
  FROM @la AS l
 WHERE l.is_laal = 1;

INSERT INTO dbo.lease_financing_audit_log (   LEASE_APPLICATION
                                            , CHANGED_DATE
                                            , BASE_PAYMENT
                                          )
SELECT @lease_application
     , l.change_date
     , l.base_amount
  FROM @la AS l
 WHERE l.is_laal = 0;

SELECT @actual = post_approval_payment_amount
  FROM [dbo].[post_approval_payment_amount](@lease_application);

IF (@expected <> @actual) SELECT 'Test 4 failed'
ELSE SELECT 'Test 4 passed';

SELECT @lease_application = '38768578'
     , @base_payment      = 453.70
     , @expected          = 453.70;

DELETE FROM @la;

INSERT INTO @la (   change_date
                  , application_status_code
                  , base_amount
                  , is_laal
                )
VALUES ('2017-05-11 03:46:26.4800000', 'L', NULL, 1)
     , ('2017-05-11 03:48:05.0600000', NULL, 200, 0)
     , ('2017-05-12 03:48:05.0600000', NULL, @base_payment, 0)
     , ('2017-06-09 12:00:36.2000000', 'X', NULL, 1)
     , ('2017-09-18 11:57:13.5100000', NULL, 100, 0);

INSERT INTO dbo.lease_applications_audit_log (   LEASE_APPLICATION
                                               , CHANGED_DATE
                                               , APPLICATION_STATUS_CODE
                                             )
SELECT @lease_application
     , l.change_date
     , l.application_status_code
  FROM @la AS l
 WHERE l.is_laal = 1;

INSERT INTO dbo.lease_financing_audit_log (   LEASE_APPLICATION
                                            , CHANGED_DATE
                                            , BASE_PAYMENT
                                          )
SELECT @lease_application
     , l.change_date
     , l.base_amount
  FROM @la AS l
 WHERE l.is_laal = 0;

SELECT @actual = post_approval_payment_amount
  FROM [dbo].[post_approval_payment_amount](@lease_application);

IF (@expected <> @actual) SELECT 'Test 5 failed'
ELSE SELECT 'Test 5 passed';

SELECT @lease_application = '38768578'
     , @base_payment      = 453.70
     , @expected          = 453.70;

DELETE FROM @la;

INSERT INTO @la (   change_date
                  , application_status_code
                  , base_amount
                  , is_laal
                )
VALUES ('2017-05-11 03:46:26.4800000', 'L', NULL, 1)
     , ('2017-05-11 03:48:05.0600000', NULL, @base_payment, 0)
     , ('2017-06-09 12:00:36.2000000', 'X', NULL, 1)
     , ('2017-09-18 11:57:13.5100000', NULL, 100, 0);

INSERT INTO dbo.lease_applications_audit_log (   LEASE_APPLICATION
                                               , CHANGED_DATE
                                               , APPLICATION_STATUS_CODE
                                             )
SELECT @lease_application
     , l.change_date
     , l.application_status_code
  FROM @la AS l
 WHERE l.is_laal = 1;

INSERT INTO dbo.lease_financing_audit_log (   LEASE_APPLICATION
                                            , CHANGED_DATE
                                            , BASE_PAYMENT
                                          )
SELECT @lease_application
     , l.change_date
     , l.base_amount
  FROM @la AS l
 WHERE l.is_laal = 0;

SELECT @actual = post_approval_payment_amount
  FROM [dbo].[post_approval_payment_amount](@lease_application);

IF (@expected <> @actual) SELECT 'Test 6 failed'
ELSE SELECT 'Test 6 passed';

DROP TABLE [dbo].[LEASE_APPLICATIONS_AUDIT_LOG];
GO

DROP TABLE [dbo].[LEASE_FINANCING_AUDIT_LOG];
GO

DROP FUNCTION [dbo].[post_approval_payment_amount];
GO

【问题讨论】:

  • 我不能把它放在小提琴上,因为它的长度超过了 8k 个字符。当我试图最小化代码时,它还有其他抱怨
  • 尝试 rextester.com dbfiddle.uk db-fiddle.com 如果有 6 个测试,请指定它们是什么。你的问题不是很清楚。
  • @Used_By_Already 我无法将我的查询导入到您提供的任何服务中。但是我修改了查询。将其复制/粘贴到 SSMS(在任何数据库上)并执行它。您将了解测试的含义。

标签: sql sql-server tsql sql-server-2014


【解决方案1】:

您询问了有关替代方法的一些想法。您可以单独或组合使用这些想法:

  1. 请记住,可以使用运算符其他而不是等号来完成 JOIN,因此您可以例如连接您的表 ON LEASE_APPLICATIONS_AUDIT_LOG.CHANGED_DATE &lt;= LEASE_FINANCING_AUDIT_LOG.CHANGED_DATE
  2. LAG 和 LEAD 函数是检索“附近”记录的高度优化函数。第三个参数甚至允许您指定在未找到记录时将返回的值 - 一个方便的位置插入您的 CAST('9999-12-31 23:59:59' AS DATETIME 默认值。
  3. 永远不要低估正确使用公用表表达式(使用 WITH 子句)可以实现的清晰度和性能。

【讨论】:

  • 我知道所有这些要点。但是,我无法使用 LEAD 功能获得相同的性能。正如我所说,这些查询目前提供了我能找到的最佳性能,但我不太喜欢它的编写方式。
  • 你不喜欢什么?行太多?嵌套?某些关键字?如果您通过修改查询来解释您想要实现的目标,将会很有帮助。
猜你喜欢
  • 1970-01-01
  • 2015-03-28
  • 1970-01-01
  • 2019-12-23
  • 1970-01-01
  • 2021-04-16
  • 1970-01-01
  • 2012-06-02
  • 1970-01-01
相关资源
最近更新 更多