MySQL查询从不同的值中查找计数

时间:2018-05-16 08:51:05

标签: mysql

我有一张包含58k交易记录的表格。我想仅返回在日期参数(通常为1天)中有3次拒付付款的payment_ref(客户ID)。付款一天尝试3次,只要其中一个获得授权,我很高兴。它在一天内下降了3次,我需要追逐。我现在的查询是

SELECT DISTINCT(cp.payment_ref) as ref
              , (SELECT COUNT(id) 
                   FROM client_payments 
                  WHERE status LIKE 'Declined' 
                    AND payment_ref = ref) as declined 
           FROM client_payments as cp 
          WHERE cp.payment_date BETWEEN '2018-05-14 00:00:00' AND '2018-05-14 23:59:59'

但查询花了很多年,计数大于3(嵌入式查询似乎在整个表上搜索),我在所有搜索字段上都有索引。该表格如下: -

name , payment_ref, timestamp          , status
smith, 123        , 2018-05-15 10:12:22, Declined
smith, 123        , 2018-05-15 14:12:22, Declined
smith, 123        , 2018-05-15 19:12:22, Declined
john , 246        , 2018-05-15 10:12:22, Declined
john , 246        , 2018-05-15 14:12:22, Authorised (OK, 2nd payment is auth'd)
jones, 135        , 2018-05-15 10:00:22, Authorised (OK, 1st payment is auth'd)

我哪里错了?

4 个答案:

答案 0 :(得分:0)

我发现不需要依赖子查询来获取计数,您可以从主查询中获取它并去除所需计数的不同使用聚合

SELECT cp.payment_ref, COUNT(cp.id) cnt
FROM client_payments AS cp 
WHERE cp.payment_date BETWEEN '2018-05-14 00:00:00' AND '2018-05-14 23:59:59'
AND cp.`status` = 'Declined'
GROUP BY cp.payment_ref
HAVING cnt >= 3

同时检查explain plan查询并搜索是否使用了键/索引

答案 1 :(得分:0)

似乎只在count子查询中需要时间范围。

为什么不将where语句移动到子查询中?这应该会大大减少运行时间:

SELECT DISTINCT(cp.payment_ref) as ref,
 (SELECT COUNT(id) FROM client_payments 
  WHERE status LIKE 'Declined' 
  AND payment_ref = ref
  AND payment_date BETWEEN '2018-05-14 00:00:00' AND '2018-05-14 23:59:59') as declined 
 FROM client_payments as cp 

答案 2 :(得分:0)

确定您在payment_reftimestampstatus上有索引后,我会通过自我加入来解决这个问题:

SELECT @starttimestamp:='2018-05-14 00:00:00';
SELECT @endtimestamp:='2018-05-14 23:59:59';

SELECT
  first.payment_ref,
  first.timestamp, first.name, first.status,
  second.timestamp, second.name, second.status,
  third.timestamp, third.name, third.status
FROM
  transactions AS first

  -- find a later declined transaction
  INNER JOIN transactions AS second
    ON first.payment_ref=second.payment_ref
    AND first.timestamp<second.timestamp
    AND second.timestamp<=@endtimestamp
    AND second.status LIKE 'Declined%'

  -- find an even later declined transaction
  INNER JOIN transactions AS third
    ON second.payment_ref=third.payment_ref
    AND second.timestamp<third.timestamp
    AND third.timestamp<=@endtimestamp
    AND third.status LIKE 'Declined%'

WHERE first.timestamp BETWEEN @starttimestamp AND @endtimestamp
AND first.status LIKE 'Declined%'
;

这可以最佳地使用指数,并通过关键范围扫描具有非常高的选择性

如果你真的需要逐行格式,你可以通过一个只运行一次的包装器查询来转换它。

答案 3 :(得分:0)

感谢您的回复。最快的运行查询如下.08秒

SELECT cp.payment_ref as ref,count(status='Declined') as no_declined FROM client_payments as cp WHERE cp.payment_date BETWEEN '2018-05-14 00:00:00' AND '2018-05-14 23:59:59' GROUP BY cp.payment_ref HAVING COUNT(status='Declined')>2