【问题标题】:Query to display spent credits from transactional table查询以显示交易表中的已用积分
【发布时间】:2018-04-24 18:06:26
【问题描述】:

我正在处理一个包含信用交易的表格,我希望在该表格中显示在进行销售时花费了谁的信用。

在表格中:

  • Credits 由实体使用唯一的实体代码添加(记录在GivenByUserCode 列中)
  • 信用添加总是有这样的代码。
  • 已花费的积分将始终为负值。
  • 已花费的积分将没有实体代码(GivenByUserCode 的值为 null)。

以上述数据为例,如果用户在2018-01-02 上进行购买,则报告应显示来自BM01 的所有这些积分。增加的复杂性在于,一次购买可以分成多个添加,请参阅2018-02-03 上的购买,它分为 3 个添加。

我认为解决方案与使用 cteover 有关,但我没有使用这些的经验。我确实在SqlServerCentral 上发现了类似(不一样)的问题。

任何帮助/指导将不胜感激。


输入和 DDL

DECLARE @CreditLogs TABLE(CreditLogId int not null identity(1,1), Credits INT NOT NULL, OccurredOn DATETIME2(7) NOT NULL, GivenByUserCode VARCHAR(100) NULL)

INSERT INTO @CreditLogs (Credits, OccurredOn, GivenByUserCode) VALUES
  (10,  '2018-01-01', 'BM01')
, (10,  '2018-01-01', 'BM01')
, (-10, '2018-01-02', NULL)
, (-5,  '2018-01-04', NULL)
, (5,   '2018-02-01', 'SP99')
, (40,  '2018-02-02', 'BM02')
, (-40, '2018-02-03', NULL)
, (-4,  '2018-03-05', NULL)

表格输入

CreditLogId | Credits | OccurredOn | GivenByUserCode
------------+---------+------------+----------------
          1 |      10 | 2018-01-01 |            BM01
          2 |      10 | 2018-01-01 |            BM01
          3 |     -10 | 2018-01-02 |            NULL
          4 |      -5 | 2018-01-04 |            NULL
          5 |       5 | 2018-02-01 |            SP99
          6 |      40 | 2018-02-02 |            BM02
          7 |     -40 | 2018-02-03 |            NULL
          8 |      -4 | 2018-03-05 |            NULL

预期输出

SELECT *
FROM (VALUES
     (3, '2018-01-02', 10, 'BM01')
    ,(4, '2018-01-04', 5, 'BM01')
    ,(7, '2018-02-03', 5, 'BM01')
    ,(7, '2018-02-03', 5, 'SP99')
    ,(7, '2018-02-03', 30, 'BM02')
    ,(8, '2018-03-05', 4, 'BM02')
) expectedOut (CreditLogId, OccurredOn, Credits, GivenByUserCode)

产生输出

CreditLogId | Occurred on | Credits | GivenByUserCode
------------+-------------+---------+----------------
          3 |  2018-01-02 |      10 |            BM01
          4 |  2018-01-04 |       5 |            BM01
          7 |  2018-02-03 |       5 |            BM01
          7 |  2018-02-03 |       5 |            SP99
          7 |  2018-02-03 |      30 |            BM02
          8 |  2018-03-05 |       4 |            BM02

到目前为止的代码

这并不多,我不知道从这里去哪里。

WITH totals AS (
    SELECT CreditLogId, OccurredOn, credits, sum(credits) OVER(ORDER BY OccurredOn) AS TotalSpent
    FROM @CreditLogs
    WHERE Credits < 0
)
SELECT *
FROM totals

补充说明

预期输出是针对这些信用来源的每个已用信用金额。积分以先进先出 (FIFO) 的方式使用。这里对示例输出中的每个值进行了解释,希望能够阐明所需的输出。

  • 对于 10 个信用(信用日志 id 3)的支出可以追溯到信用日志 id 1 的添加
  • 对于 5 个信用(信用记录 id 4)的支出,可以追溯到信用记录 2 的添加(因为信用记录 id 1 已“用完”)
  • 对于在信用日志 id 7 中花费 40 信用可以追溯到
    • 从信用记录 id 2 中添加的剩余部分,5 个信用
    • 信用记录ID 5(加5)
    • 信用记录 id 6(添加 40 剩余 10)
  • 对于信用记录 8 中的 4 个信用的支出,使用信用记录 id 6 的余额

请注意,剩余的总余额为 6 个积分,余额不必归零,但永远不会为负数,因为用户只能花费他们所拥有的。

【问题讨论】:

  • 您的预期输出与输入数据不匹配,您能否看看它是否正确或解释您是如何得出输出的?
  • @PankajGadge - 预期输出是针对这些积分来自的每个已用积分金额。积分以先进先出的方式使用。我将更新问题以使其更清楚。
  • @PankajGadge - 请参阅末尾添加的Additional clarification。如果这仍然引起问题,请告诉我。
  • @scsimon - 我们预计此表中的记录每年将增长 4 到 500 万条(表/历史永远不会被清除)。
  • 那么 Adam Silenko 的答案可能是你能得到的最好的答案,因为有一个基于相等性的附加连接条件和每个订​​单的少量行数。

标签: sql sql-server sql-server-2012


【解决方案1】:

试试这个:

WITH Credits_added AS (
    SELECT CreditLogId, OccurredOn, credits
    , SUM(credits) OVER (ORDER BY CreditLogId) - credits AS b --before
    , SUM(credits) OVER (ORDER BY CreditLogId) AS a --after
    , GivenByUserCode
    FROM @CreditLogs
    WHERE Credits > 0)
, Credits_spent AS (
    SELECT CreditLogId, OccurredOn, credits
    , SUM(credits) OVER (ORDER BY CreditLogId) * -1 + credits AS b
    , SUM(credits) OVER (ORDER BY CreditLogId) * -1 AS a
    FROM @CreditLogs
    WHERE Credits < 0)
SELECT s.CreditLogId, s.OccurredOn
, CASE WHEN a.a > s.a THEN s.a ELSE a.a END - CASE WHEN a.b > s.b THEN a.b ELSE s.b END AS Credits 
, a.GivenByUserCode
FROM Credits_added AS a
INNER JOIN Credits_spent AS s ON a.a > s.b AND s.a > a.b

【讨论】:

  • 绝妙的答案!我暂时将其标记为这样,下周我仍然需要在办公室使用一些真实数据对其进行审查。非常感谢!
  • 如果你想保存输出,并更快地生成下一个报告,那么你可以添加到输出选择, a.CreditLogId,并使用输出来设置每行使用的积分
  • (ORDER BY CreditLogId) 应更改为(ORDER BY CreditLogId ROWS UNBOUNDED PRECEDING),否则默认为RANGE UNBOUNDED PRECEDING,这往往会降低效率(在这种情况下,CreditLogId 是唯一的优化器)。
【解决方案2】:

由于您提到每年将有 4 到 5 百万条记录,即使这可以通过查询来完成,它也会是一个缓慢的查询。

我建议使用另一个表,例如 creditSpent,其中包含 (PurchaseCreditLogId , additionCreditLogId, Amount)

在插入购买时,查找所有记录,计算每条记录应减少的金额并将该信息存储在该表中

然后,当您运行报表时,您可以对该表进行简单查询

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-10
    • 2022-07-16
    • 2017-09-12
    • 2021-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-26
    相关资源
    最近更新 更多