【问题标题】:MySQL query to get last N failed paymentsMySQL 查询以获取最后 N 次失败的付款
【发布时间】:2019-06-07 09:34:05
【问题描述】:

我的数据库有两个表:userspaymentsuserspayments 之间存在一对多的关系:每个用户可以有 o 或多个付款,并且付款属于一个用户。此外,每次付款都可能成功或失败。

我需要编写一个查询来获取最近 N 次付款失败的所有用户。

我发现了这个查询,它允许所有支付 N 次或更多(在本例中为 4 次或更多)的用户:

SELECT x.user_id, count(*) as cnt 
FROM (
    SELECT a.user_id, a.date, a.status FROM payment AS a WHERE 
        (SELECT COUNT(*) FROM payment AS b 
         WHERE b.user_id = a.user_id AND b.date >= a.date) <= 4 
         ORDER BY a.user_id ASC, a.date DESC) AS x 
WHERE x.status = 'failed' 
GROUP BY x.user_id
HAVING cnt >=4;

但我不能让它适用于一个确切的数字(在这个例子中,正好是 4)。

表格的结构是:

  • 用户:id、姓名、电子邮件、密码、created_at、updated_at
  • 付款:id、日期(付款所属的日期)、状态(成功、失败)、user_id、created_at、updated_at

一个例子:

这个sqlfiddle 可能有助于了解我需要什么。它应该只返回用户 4(即最近 4 次付款失败的用户,但也返回用户 5(有 5 次付款失败的用户)。

相同的 DDL:

CREATE TABLE users
    (`id` int, `name` varchar(6), `email` varchar(7), `password` varchar(10), `created_at` timestamp, `updated_at` timestamp)
;

INSERT INTO users
    (`id`, `name`, `email`, `password`)
VALUES
    (1, 'name 1', 'email 1', 'password 1'),
    (2, 'name 2', 'email 2', 'password 2'),
    (3, 'name 3', 'email 3', 'password 3'),
    (4, 'name 4', 'email 4', 'password 4'),
    (5, 'name 5', 'email 5', 'password 5')
;


CREATE TABLE payments
    (`id` int, `date` varchar(10), `status` varchar(7), `user_id` int ,`created_at` timestamp, `updated_at` timestamp)
;

INSERT INTO payments
    (`id`, `date`, `status`, `user_id`)
VALUES
    (1, '2019-01-01', 'success', 1),
    (2, '2019-01-01', 'failed', 2),
    (3, '2019-01-01', 'failed', 3),
    (4, '2019-01-01', 'success', 4),
    (5, '2019-01-01', 'success', 5),
    (6, '2019-01-02', 'success', 1),
    (7, '2019-01-02', 'success', 2),
    (8, '2019-01-02', 'success', 3),
    (9, '2019-01-02', 'success', 4),
    (10, '2019-01-02', 'success', 5),
    (11, '2019-01-03', 'success', 1),
    (12, '2019-01-03', 'failed', 2),
    (13, '2019-01-03', 'success', 3),
    (14, '2019-01-03', 'failed', 4),
    (15, '2019-01-03', 'failed', 5),
    (16, '2019-01-04', 'success', 1),
    (17, '2019-01-04', 'failed', 2),
    (18, '2019-01-04', 'failed', 3),
    (19, '2019-01-04', 'failed', 4),
    (20, '2019-01-04', 'failed', 5),
    (21, '2019-01-05', 'success', 1),
    (22, '2019-01-05', 'failed', 2),
    (23, '2019-01-05', 'failed', 3),
    (24, '2019-01-05', 'failed', 4),
    (25, '2019-01-05', 'failed', 5),
    (26, '2019-01-06', 'success', 1),
    (27, '2019-01-06', 'success', 2),
    (28, '2019-01-06', 'failed', 3),
    (29, '2019-01-06', 'failed', 4),
    (30, '2019-01-06', 'failed', 5),
    (31, '2019-01-07', 'failed', 5)
;

【问题讨论】:

  • 您想要那些最后一次未失败的用户是 5 次付款之前,和/或那些只有 4 次付款的用户,所有这些都失败了。
  • 感谢您的回复@Strawberry。我想要最后 N 次付款(最近 N 次付款)失败的所有用户。因此,在示例中,如果我想要最后 4 次付款失败的用户列表,则唯一满足该要求的用户是用户 4(用户 4 的所有付款:成功、成功、失败、失败、失败、失败。最后 4失败了,所以我希望它在结果集中)

标签: php mysql


【解决方案1】:

我已经用一个更简单的 SQL 语句更新了你的 SQL Fiddle,它可以产生正确的结果。如果您只需要特定数量的失败付款的用户,请将 >= 更改为 =

SQL Fiddle

MySQL 5.6 架构设置

CREATE TABLE users
    (`id` int, `name` varchar(6), `email` varchar(7), `password` varchar(10), `created_at` timestamp, `updated_at` timestamp)
;

INSERT INTO users
    (`id`, `name`, `email`, `password`)
VALUES
    (1, 'name 1', 'email 1', 'password 1'),
    (2, 'name 2', 'email 2', 'password 2'),
    (3, 'name 3', 'email 3', 'password 3'),
    (4, 'name 4', 'email 4', 'password 4'),
    (5, 'name 5', 'email 5', 'password 5')
;


CREATE TABLE payments
    (`id` int, `date` varchar(10), `status` varchar(7), `user_id` int ,`created_at` timestamp, `updated_at` timestamp)
;

INSERT INTO payments
    (`id`, `date`, `status`, `user_id`)
VALUES
    (1, '2019-01-01', 'success', 1),
    (2, '2019-01-01', 'failed', 2),
    (3, '2019-01-01', 'failed', 3),
    (4, '2019-01-01', 'success', 4),
    (5, '2019-01-01', 'success', 5),
    (6, '2019-01-02', 'success', 1),
    (7, '2019-01-02', 'success', 2),
    (8, '2019-01-02', 'success', 3),
    (9, '2019-01-02', 'success', 4),
    (10, '2019-01-02', 'success', 5),
    (11, '2019-01-03', 'success', 1),
    (12, '2019-01-03', 'failed', 2),
    (13, '2019-01-03', 'success', 3),
    (14, '2019-01-03', 'failed', 4),
    (15, '2019-01-03', 'failed', 5),
    (16, '2019-01-04', 'success', 1),
    (17, '2019-01-04', 'failed', 2),
    (18, '2019-01-04', 'failed', 3),
    (19, '2019-01-04', 'failed', 4),
    (20, '2019-01-04', 'failed', 5),
    (21, '2019-01-05', 'success', 1),
    (22, '2019-01-05', 'failed', 2),
    (23, '2019-01-05', 'failed', 3),
    (24, '2019-01-05', 'failed', 4),
    (25, '2019-01-05', 'failed', 5),
    (26, '2019-01-06', 'success', 1),
    (27, '2019-01-06', 'success', 2),
    (28, '2019-01-06', 'failed', 3),
    (29, '2019-01-06', 'failed', 4),
    (30, '2019-01-06', 'failed', 5),
    (31, '2019-01-07', 'failed', 5)
;

查询 1

SELECT x.user_id, count(*) as cnt 
FROM (
    SELECT a.user_id, a.date, a.status FROM payments AS a WHERE 
        (SELECT COUNT(*) FROM payments AS b 
         WHERE b.user_id = a.user_id AND b.date >= a.date) <= 4 
         ORDER BY a.user_id ASC, a.date DESC) AS x 
WHERE x.status = 'failed' 
GROUP BY x.user_id
HAVING cnt >=4

Results

| user_id | cnt |
|---------|-----|
|       4 |   4 |
|       5 |   4 |

查询 2

SELECT `user_id`, count(*) as `cnt`
FROM `payments` 
WHERE `status` = 'failed'
GROUP BY `user_id`
HAVING cnt >= 4
ORDER BY `cnt`

Results

| user_id | cnt |
|---------|-----|
|       2 |   4 |
|       3 |   4 |
|       4 |   4 |
|       5 |   5 |

【讨论】:

  • 我很困惑。 user_id 2为0失败,user_id 3为3失败,4为4失败,5为5失败!?!
  • 感谢您的回复@Sloan Thrasher。确实,用户 2 有 4 次付款失败。但是,如果我们检查用户 2 最近的 4 次付款,我们会得到:失败、失败、失败和成功。我的意图是获得最后(或最近)4 次失败的付款。所以用户 2 不应该是结果集的一部分。
【解决方案2】:

未充分测试...(并且,正如所写,专门用于 8.0 之前的 MySQL 版本)

SELECT u.*
  FROM users u
  LEFT 
  JOIN
     ( SELECT user_id
            , status
            , CASE WHEN @prev = user_id THEN @i:=@i+1 ELSE @i:=1 END i
            , @prev:=user_id 
         FROM payments
            , (SELECT @prev:=null,@i:=0) vars 
        ORDER 
           BY user_id
            , date DESC
     ) x 
    ON x.user_id = u.id
   AND (status = 'success' AND i <= 4) OR (status = 'failed' AND i=5)
 WHERE x.user_id IS NULL;

【讨论】:

  • 我有 MySQL 5.7.21
  • 那真是幸运
猜你喜欢
  • 2012-02-06
  • 2014-04-01
  • 1970-01-01
  • 2013-11-09
  • 2021-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多