我有一张包含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)
我哪里错了?
答案 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_ref
,timestamp
和status
上有索引后,我会通过自我加入来解决这个问题:
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