【问题标题】:Return data where the running total of amounts 30 days from another row is greater than or equal to the amount for that row返回另一行 30 天的累计金额大于或等于该行金额的数据
【发布时间】:2020-12-27 08:02:48
【问题描述】:

假设我是一个包含日期、帐户、产品、购买类型和金额的表格,如下所示:

查看此表,您可以看到对于任何特定的帐户/产品组合,都有买入和卖出。从本质上讲,我想写的是一个 SQL 查询,它标记以下内容:是否有账户以一定数量购买,然后在购买后 30 天内卖出相同或更多的总金额?

例如,我们可以看到帐户 1 在 8 月 1 日以 20k 的价格购买了产品 A。如果我们查看账户 1 在接下来 30 天内对产品 A 的销售总和,我们会看到他们总共售出了 20k - 与最初的购买相同:

理想情况下,查询将返回标记所有这些实例的结果:对于每个单独的购买,查找该产品/帐户在购买后 30 天的所有销售,并且只返回销售总和为的行大于或等于初始购买次数

编辑:使用提供的示例数据,所需的外观应该或多或少如下所示:

您会看到产品 B/帐户 2 的 8 月 2 日购买未返回,因为该产品/帐户/购买组合在接下来 30 天内的销售总和不等于或超过购买金额35k,但它确实返回了产品 B/账户 2 的 8/3 买入行,因为卖出确实超过了 10k 的买入金额。

我知道我需要将销售与购买结合起来,其中帐户/产品相等且 datediff 小于或等于 30,并且我基本上已将那部分结构化。我似乎无法工作的是运行总额部分,并且仅在该总额大于或等于该购买时才返回数据。我知道我可能需要对运行总和使用 over/partition by 子句,但我正在努力产生正确的结果/正确优化。对此的任何帮助将不胜感激 - 只是寻找一些关于如何解决这个问题的一般方向。

奖励:一旦运行总数超过买入,停止返回卖出的效果会更加强大,例如,我提供的所需输出中的最后两行在技术上是不需要的 - 因为前两行买入后的卖出量已经超过了买入量。

【问题讨论】:

  • 1.请不要将数据(或代码)作为图像发布,而应使用格式化文本。 2. 向我们展示你的尝试...
  • 如果30天内又有“买”怎么办?
  • @GMB 查询旨在查看每个单独的购买,因此如果同一帐户/产品有多个购买,我们将只查看每个购买 30 天后的销售(即,如果购买发生在 9 月 1 日,我们会查看该帐户/产品在 9 月 2 日之前的相应销售量,看看总和是否超过 9 月 1 日的购买量。如果同一帐户/产品在 9 月 2 日购买,我们会做同样的事情,直到 10 月 2 日或从该日期起的任何 30 天。
  • @DaleK - 礼仪方面的道歉,我不经常使用这个
  • 不要道歉 - 只是更新/改进您的问题 :)

标签: sql sql-server datetime sum window-functions


【解决方案1】:

在 SQL Server 中,一个选项使用横向连接:

select 
    t.*, 
    case when t.amount = x.amount then 1 else 0 end as is_returned
from mytable t
cross apply (
    select sum(amount) amount
    from mytable t1
    where 
        t1.purchase_type = 'Sell'
        and t1.account = t.account 
        and t1.product = t.product
        and t1.date >= t.date
        and t1.date <= dateadd(day, 30, t.date)
) x
where t.purchase_type = 'Buy'

横向连接将同一账户和产品在接下来的 30 天内的“卖出”金额相加,然后您可以将其与买入金额进行比较。该查询为您每次购买提供一行,并带有一个布尔标志,指示金额是否匹配。

在支持窗口函数范围规范的数据库中,这将更有效地用窗口和表示:

select *
from (
    select 
        t.*,
        case when amount = sum(case when purchase_type = 'Sell' then amount end) over(
            partition by account, product
            order by date
            range between current row and interval '30' day following
        ) then 1 else 0 end as is_returned
    from mytable t
) t
where purchase_type = 'Buy'

编辑:这将生成一个类似于您问题中的第三个表的结果集:

select t.*, x.*
from mytable t
cross apply (
    select 
        t1.date sale_date, 
        t1.amount sell_amount, 
        sum(t1.amount) over(order by t1.date) running_sell_amount,
        sum(t1.amount) over() total_sell_amount
    from mytable t1
    where 
        t1.purchase_type = 'Sell'
        and t1.account = t.account 
        and t1.product = t.product
        and t1.date >= t.date
        and t1.date <=  dateadd(day, 30, t.date)
) x
where t.purchase_type = 'Buy' and t.amount = x.total_sell_amount

【讨论】:

  • 谢谢!那很快!让我测试一下。但是,我认为对于这个用例,我希望看到每个人的销售都与所需的输出相似——而不仅仅是每次购买一行。这将如何改变上述查询?
  • @nunga:你没有显示你想要的结果,所以这是我的理解。您可能需要edit your question 才能显示预期结果。
  • 这是帖子中的第三张表。抱歉,我不太擅长构建这些。理想情况下,查询将只针对曾经聚合、超过或等于初始购买(从该购买起 30 天)的销售返回每个买入/​​卖出组合的一行。我会尝试稍微修改一下问题以改进
  • @nunga:好的,我明白了。这对我来说并不明显,这是预期的结果。请参阅我的答案中的编辑。
  • 谢谢@GMB - 是的,不清楚,抱歉。我即将对此进行测试,但是,它是否还需要一个条件来仅返回运行总计等于或大于初始购买金额的行?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-27
  • 1970-01-01
  • 1970-01-01
  • 2022-10-25
相关资源
最近更新 更多