MySQL - 是否可以使用regexp计算行数?

时间:2016-07-08 07:34:49

标签: mysql

我需要MySQL问题的帮助。我有两个表,一个名为subscription,另一个名为payment。每个订阅都有多笔付款。某些付款未启动(0),有些付款失败(1),有些付款失败(2)。

每个订阅都可以生成多次付款尝试,直到获得成功。例如:

  • 首次尝试遇到网络连接问题,并获得status = 0
  • 第二次尝试到达付款API但信用卡数据错误或资金不足,因​​此获得status = 1
  • 第三次尝试成功,获得status = 2

前两个可能超过两个,实际上它看起来像这样:

0, 1, 0, 0, 1, 1, 1, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 2, 0, 1, 2, 1, 0, 2, 0, 2, 1, 2, 2

因此,每个订阅都有多个付款顺序(每月或经常,没有必要的日期相关),可以通过这个正则表达式[0|1]*2的状态来识别,得到类似的内容:

0, 1, 0, 0, 1, 1, 1, 1, 2,   // 9
0, 0, 1, 2,                  // 4
1, 0, 1, 2,                  // 4 
2,                           // 1
2,                           // 1
0, 1, 2,                     // 3 
1, 0, 2,                     // 3
0, 2,                        // 2
1, 2,                        // 2 
2                            // 1

但是,当然,由于有多个订阅,付款行在数据库中混合,唯一连接它们的是subscription_id

我需要获得的是从首次尝试,第二次尝试,第三次尝试,第二次尝试,第10次尝试,10次尝试成功付款的订阅数量。

在上面的例子中,它应该是:

attempts  count
9         1
4         2
3         2
2         2
1         3

这可能吗?

测试数据

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";

CREATE TABLE IF NOT EXISTS `payment` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subscription_id` int(11) DEFAULT NULL,
`status` smallint(6) NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_6D28840D9A1887DC` (`subscription_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPRESSED AUTO_INCREMENT=36 ;

INSERT INTO `payment` (`id`, `subscription_id`, `status`) VALUES
(1, 1, 1),(2, 1, 2),(3, 1, 2),(4, 2, 2),(5, 3, 2),(6, 4, 2),(7, 5, 2),
(8, 6, 1),(9, 6, 2),(10, 7, 2),(11, 7, 2),(12, 8, 0),(13, 8, 1),(14, 8, 2),
(15, 8, 2),(16, 9, 1),(17, 9, 2),(18, 9, 2),(19, 9, 1),(20, 9, 2),(21, 10, 0),
(22, 10, 1),(23, 10, 1),(24, 10, 1),(25, 10, 1),(26, 11, 0),(27, 11, 0),(28, 11, 1),
(29, 11, 1),(30, 11, 1),(31, 8, 0),(32, 8, 1),(33, 8, 2),(34, 10, 1),(35, 10, 2);

CREATE TABLE IF NOT EXISTS `subscription` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`status` smallint(6) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPRESSED AUTO_INCREMENT=12 ;

INSERT INTO `subscription` (`id`, `status`) VALUES
(1, 1),(2, 1),(3, 1),(4, 1),(5, 1),(6, 1),(7, 1),
(8, 1),(9, 1),(10, 0),(11, 0);

ALTER TABLE `payment`
ADD CONSTRAINT `sub_id` FOREIGN KEY (`subscription_id`) REFERENCES `subscription` (`id`);

注意:测试数据与上面的示例不同。对于测试数据,结果应如下所示:

subscription_id    cntAttempts     [attempts counted]
1                  2               1, 2,
1                  1               2, 
2                  1               2, 
3                  1               2, 
4                  1               2, 
5                  1               2, 
6                  2               1, 2, 
7                  1               2, 
7                  1               2, 
8                  3               0, 1, 2, 
8                  1               2, 
9                  2               1, 2, 
9                  1               2, 
9                  2               1, 2, 
10                 7               0, 1, 1, 1, 1, 1, 2
11                 5               0, 0, 1, 1, 1, 
8                  3               0, 1, 2, 

对于subscription_id = 10,最后两笔付款是在118之后插入的。

预期的最终结果:

paymentAttemptsCount    count
1                       9
2                       5
3                       1
4                       0
5                       1
6                       0
7                       1
8                       0
9                       0
10                      0

注意:id = 11的订阅无法成功付款。

1 个答案:

答案 0 :(得分:0)

如上所述,您实际上应该为paymentid / invoicenumber(它将是发票表的外键)添加一列,以便将多个付款尝试组合在一起 - 因为它们属于一起而您的数据模型应该代表该逻辑。您的问题似乎更复杂,因为您缺少这些数据。

您可以在运行时计算该ID(至少如果您按顺序添加它们,并且没有为同一subscription_id混合付款,例如因为发票可能在下个月仍然打开,您现在尝试两个发票)。对于您的第一个查询,您可以例如使用

select subscription_id, 
       count(*) as cntAttempts, 
       group_concat(status order by id) as attempts_counted 
from 
  (SELECT id, subscription_id, status, 
         @pid := CASE WHEN @last_status = 2 or subscription_id <> @last_id 
                      THEN @pid + 1 else @pid END as pid,
         @last_id := subscription_id,
         @last_status := status
   from (select @last_id := 0, @pid := 0, @last_status := 0) init, payment
   order by subscription_id, id
  ) as payment_grpd
group by pid, subscription_id;

您的第二个查询需要再次对此结果进行分组,例如

select cntAttempts, 
       count(*) as count
from 
  (select pid, 
          count(*) as cntAttempts 
   from 
     (SELECT id, subscription_id, status, 
            @pid := CASE WHEN @last_status = 2 or subscription_id <> @last_id 
                         THEN @pid + 1 else @pid END as pid,
            @last_id := subscription_id,
            @last_status := status
      from (select @last_id := 0, @pid := 0, @last_status := 0) init, payment
      order by subscription_id, id
     ) as payment_grpd
     group by pid, subscription_id
  ) as subcounts 
group by cntAttempts;

子查询payment_grpd将重新计算您的ID。如果添加缺少的关系,则第一个查询看起来就像

select subscription_id, 
       count(*) as cntAttempts, 
       group_concat(status order by id) as attempts_counted 
from payment
group by pid, subscription_id;

和第二个类似。为了规范化,可能必须将subscription_id移动到参考表。