在这个场景中,我有两个表用户和事务.我想将指定时间段内的所有交易分为三类:第一类定期存款、第二类定期存款和附加存款.

要计算出第一笔定期存款,您需要使用created_at字段判断用户在该笔存款之前是否没有任何交易,对于第二笔定期存款,他们在该笔存款之前会有一笔其他交易,而对于其他交易,他们在该笔存款之前应该有两笔或更多交易.

transactions表有两个我们关心的字段:

  • user(用户id)
  • created_at(创建事务的时间)

这是我的try ,但我很难想象整个查询.我该怎么做有什么 idea 吗?

SELECT
  COUNT(t.id) as first_time_deposits
FROM
  transactions t
WHERE 
  status = 'approved' AND DATE(t.created_at) BETWEEN (CURDATE() - INTERVAL 0 DAY) AND CURDATE()
GROUP BY user
HAVING NOT EXISTS
  (
    SELECT
      u.id
    FROM
      transactions u
    WHERE
      u.created_at < t.created_at
  )

我在这里使用日期间隔只是为了过滤一天、一周等之间的交易.这个查询不起作用,因为我试图在子查询中引用外部查询的日期.我还缺少二次定期存款和附加存款.

我正在寻找的示例输出:

first_time_deposits second_time_deposits additional_deposits
15 5 6

在选定的时间段内执行所有操作.

任何帮助都将不胜感激.

推荐答案

我就是这样做的.例如,如果"首次"交易同时发生,该解决方案就可以正常工作.其他人也一样

  • "first_to_last"是一个递归查询,只显示我们需要获取事务的数字(在您的情况下是1到3).这使得查询很容易调整,以防您突然需要的不是前3个事务,而是前10个事务

  • "编号"-按日期对交易进行排序

  • 主查询连接前两个CTE,并用"第一"、"第二"和"第三"等词替换数字.除了硬编码值,我没有找到其他方法.

with recursive first_to_last(step) as (
  select 1
  union all 
  select step + 1 
    from first_to_last
   where step < 3 -- how many lines to display
),
numbered as (
  select dense_rank() over(partition by user_id order by created_at) rnk, created_at, user_id
    from transactions
)

select user_id,
       concat(case when f.step = 1 then 'first_deposit: '
                   when f.step = 2 then 'second_deposit: '
                   when f.step = 3 then 'third_deposit: '
              end,
              count(rnk))
  from numbered n
  join first_to_last f
    on n.rnk = f.step
 group by user_id, f.step
 order by user_id, f.step

dbfiddle

UPD.对附加问题的回答:"我只想计算所有第一、第二和任何不是第一或第二的存款"

只需删除"从第一个到最后一个"cte

with numbered as (
  select dense_rank() over(partition by user_id order by created_at) rnk, created_at, user_id
    from transactions
)

select user_id,
       concat(case when n.rnk = 1 then 'first_deposit: '
                   when n.rnk = 2 then 'second_deposit: '
                   else 'other_deposits: ' 
              end,
              count(rnk))
  from numbered n
 group by user_id, case when n.rnk = 1 then 'first_deposit: '
                   when n.rnk = 2 then 'second_deposit: '
                   else 'other_deposits: ' 
              end
 order by user_id, rnk

UPD2.3列输出:第一列、第二列和其他列

with numbered as (
  select dense_rank() over(partition by user_id order by created_at) rnk, created_at, user_id
    from transactions
)

select 
       sum(case when n.rnk = 1 then 1 else 0 end) first_deposit,
       sum(case when n.rnk = 2 then 1 else 0 end)  second_deposit,
       sum(case when n.rnk not in (1,2) then 1 else 0 end) other_deposit
  from numbered n

Mysql相关问答推荐

将值代入子查询

使用JSON_CONTAINS搜索多个值的原理

MySQL-如何检测时间戳中的一天

Mysql - Select 匹配某些条件的两条记录之间经过的最大天数

我可以在姓名和姓名日期之间进行搜索吗?

发生的原因及解决方法

实体内约束的唯一增量 ID 生成

MySQL - 单词边界的正则表达式,不包括下划线(连接标点符号)

当用它的别名替换 (MAX(s.salary) - MIN(s.salary) 它将不起作用.. 为什么?

按 SQL 删除分组前的重复项

为什么 fetch 方法不能获取任何东西?(feat node.js,restAPI)

处理 Visits 表中数百万行的最佳方法是什么?

WHERE SQL 语句中的列顺序是否重要

在 MySQL 中重命名外键列

PHP 判断 NULL

按日期和时间降序排序?

一次查询 MySQL 插入多行

#1030 - 从存储引擎 Aria 收到错误 176读取错误校验和的页面

mysql错误'TYPE = MyISAM'

匹配 IN 子句中的所有值