【问题标题】:SQL Query to find if most recent records are all of the same typeSQL 查询以查找最近的记录是否都属于同一类型
【发布时间】:2014-01-25 01:09:52
【问题描述】:

我有一个transaction 表,其架构类似于

id | order_id | response | amount
1  | 2        |'payment' | 1000
2  | 5        |'declined'| 0
3  | 5        |'declined'| 0
4  | 5        |'payment' | 500
5  | 5        |'declined'| 0
6  | 11       |'declined'| 0
7  | 11       |'declined'| 0
9  | 11       |'declined'| 0

我想做的是找到所有订单,其中该订单的三个最新交易被“拒绝”。假设 id 越高,交易越新(或者您可以假设有 created_at 列)。

在上述情况下,应该返回的唯一 order_id 是 11,因为 order_id 5 有 3 笔被拒绝的交易,最近的 3 笔交易是 D P D

是否有一种干净的方法可以使用在合理的时间内运行的纯 sql 来执行此操作(假设 ~50M 行)。

【问题讨论】:

  • 您表中的记录似乎没有在任何地方记录其时间戳...您如何建议确定哪些是“最新的”?
  • ...并添加更多示例数据。在开发查询时,您要确定它涵盖所有情况,对吗?因此,您的示例数据至少应该包含正确查询返回的数据。
  • 我编辑了问题,因此您可以假设存在 created_at 列或 id 列随时间单调增加。它对解决方案应该没有太大影响。

标签: mysql


【解决方案1】:

假设 id 越高越近:

SELECT t0.order_id 
FROM transaction t0 
JOIN transaction t1 ON
   ((t1.response=t0.response) AND (t1.order_id=t0.order_id) AND 
   t1.id=(SELECT MAX(id) FROM transaction WHERE id<t0.id and t0.order_id=order_id)) 
JOIN transaction t2 ON 
   ((t2.response=t0.response) AND (t2.order_id=t0.order_id) AND 
   t2.id=(SELECT MAX(id) FROM transaction WHERE id<t1.id AND t0.order_id=order_id)) 
WHERE t0.response='declined' AND 
   t0.id=(SELECT MAX(id) FROM transaction WHERE order_id=t0.order_id);

【讨论】:

  • 这很好用并且可以立即运行。唯一的缺点是它不是很灵活,因为如果你想搜索 4 个拒绝,你必须编写一个新的查询。
  • 你是对的。但它可以很容易地扩展:... JOIN transaction t3 ON ((t3.response=t0.response) AND (t3.order_id=t0.order_id) AND t3.id=(SELECT MAX(id) FROM transaction WHERE id
【解决方案2】:

根本不是一个快速的解决方案,但它应该给你你想要的(我假设最近的交易是id列价值更高的交易):

SELECT * FROM 
    (
    SELECT  *,
     (
        SELECT COUNT(1) 
        FROM `transaction` a WHERE a.order_id = b.order_id AND
        a.id >= b.id 
     )as num
    FROM `transaction`b
    ) a WHERE num =3
    AND NOT EXISTS
      (
       SELECT NULL FROM `transaction` b where response<>'declined' 
       and b.order_id = a.order_id and b.id >=a.id
      )

【讨论】:

  • 我确认这给出了正确的结果。问题是它有点慢。返回大约 50 行需要几分钟。
  • 作为参考,此查询首先将新近度分配给每个事务,然后限制到第三个最近的事务。然后 where 子句验证所有更新或等于第三个最近交易的交易是否被“拒绝”。这是一个聪明的解决方案,其中“3”参数很灵活,而且很容易修改。不幸的是,它有点慢,但却是一个非常聪明的有趣技术。感谢您花时间回答,我确实学到了一些东西。
【解决方案3】:

这是一种方法...

SELECT DISTINCT a.order_id
           FROM 
              ( SELECT x.*
                     , COUNT(*) rank 
                  FROM my_table x 
                  JOIN my_table y 
                    ON y.order_id = x.order_id 
                   AND y.id >= x.id 
                 GROUP 
                    BY id HAVING COUNT(*) <= 3
              ) a
           LEFT
           JOIN
              ( SELECT x.*
                     , COUNT(*) rank 
                  FROM my_table x 
                  JOIN my_table y 
                    ON y.order_id = x.order_id 
                   AND y.id >= x.id 
                 GROUP 
                    BY id HAVING COUNT(*) <= 3
              ) b
             ON b.order_id = a.order_id
            AND b.response <> 'declined'
          WHERE b.id IS NULL;

相同的小提琴:http://www.sqlfiddle.com/#!2/386aa3/1

只是一个想法...这行得通吗(未经测试)...

SELECT DISTINCT x.order_id
  FROM my_table x
  JOIN my_table y
    ON y.order_id = x.order_id
   AND y.id >= x.id
 GROUP
    BY x.id
HAVING COUNT(*) = 3 
   AND COUNT(*) = SUM(CASE WHEN y.response = 'declined' THEN 1 ELSE 0 END);

【讨论】:

  • 这太慢了,我不确定我能否验证它是否正确。
  • 是的 - 它可能很慢。对此感到抱歉 - 当结果少于 3 个但所有结果都被拒绝时,也不太确定该怎么做。
  • 请注意,这个解决方案会稍微受益于 (order_id,response) 上的复合索引 - 但是当结果少于 3 个时它仍然不知道该怎么做(因为你没有说)
猜你喜欢
  • 1970-01-01
  • 2012-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多