在SQL中的特定事件之前获取所有记录

时间:2018-10-05 07:52:40

标签: mysql sql

我有一个像这样的表DATA

+--------+--------+----------+------+
| TranID | CustID | TransSeq | Type |
+--------+--------+----------+------+
|    1   |  100   |      1   | A    |
|    2   |  100   |      2   | A    |
|    3   |  100   |      3   | B    |
|    4   |  200   |      1   | A    |
|    5   |  200   |      2   | B    |
|    6   |  200   |      3   | A    |
|    7   |  200   |      4   | A    |
|    8   |  200   |      5   | A    |
+--------+--------+----------+------+

我想获取Type B之前的所有记录。因此,我的输出将是这样

+--------+--------+----------+------+
| TranID | CustID | TransSeq | Type |
+--------+--------+----------+------+
|    1   |  100   |      1   | A    |
|    2   |  100   |      2   | A    |
|    4   |  200   |      1   | A    |
+--------+--------+----------+------+

我能想到的一种方法是

步骤1-创建存储CustID和TransSeq的临时表,其中类型== B

CREATE TABLE TEMP AS
select CustID, TransSeq as TransSeq_B from DATA 
where Type = "B"

第1步的输出如下所示

+--------+------------+------+
| CustID | TransSeq_B | Type |
+--------+------------+------+
|  100   |      3     | B    |
|  200   |      2     | B    |
+--------+------------+------+

第2步-使用CustID将TEMP与DATA合并

CREATE TABLE DATA_NEW AS
select D.TranID, D.CustID, D.TransSeq, D.Type, T.TransSeq_B
from DATA inner join TEMP on D.CustID = T.CustID

第2步的输出如下所示

+--------+--------+----------+------+------------+
| TranID | CustID | TransSeq | Type | TransSeq_B |
+--------+--------+----------+------+------------+
|    1   |  100   |      1   | A    |       3    |
|    2   |  100   |      2   | A    |       3    |
|    3   |  100   |      3   | B    |       3    |
|    4   |  200   |      1   | A    |       2    |
|    5   |  200   |      2   | B    |       2    |
|    6   |  200   |      3   | A    |       2    |
|    7   |  200   |      4   | A    |       2    |
|    8   |  200   |      5   | A    |       2    |
+--------+--------+----------+------+------------+

第3步-从第2步查询此新表,并保留所有TransSeq都小于TransSeq_B的记录

select * from DATA_NEW
where TransSeq < TransSeq_B

有很多记录(> 20M)

,有什么有效的方法吗?

3 个答案:

答案 0 :(得分:2)

一种方法使用EXISTS查询。下面的EXISTS子句为表中的每个记录检查具有相同CustID值的所有其他记录,以检查是否存在类型为B的较早记录。如果没有,那么该记录将被添加到结果集中。

SELECT *
FROM DATA d1
WHERE
    d1.Type = 'A' AND
    NOT EXISTS (SELECT 1 FROM DATA d2
                WHERE d1.CustID = d2.CustID AND d2.TranID < d1.TranID AND
                      d2.Type = 'B');

enter image description here

Demo

答案 1 :(得分:2)

基本上,您的想法很正确-唯一需要做的就是代替创建新表,而将相关查询用作JOIN中的Views:

SELECT
  beforeB.*
FROM
  Table1 AS beforeB
  INNER JOIN (
    SELECT 
      CustID,
      MIN(TransSeq) AS TransSeq
    FROM Table1
    WHERE Type='B'
    GROUP BY CustID
  ) AS theB
  ON beforeB.CustID=theB.CustID
WHERE
  beforeB.TransSeq<theB.TransSeq

强制性SQLfiddle here

说明:theB视图从表中为每个客户过滤B型事件。它与作为选择器的客户ID上的交易表连接,仅保留具有较低TransSqq的行。

EXISTS相比,这可能更有效,因为如果可以将JOIN ed视图保留在RAM中,则根据结果集的大小和索引,无需为每行运行子查询。在查询期间。

答案 2 :(得分:0)

可能会自行加入表格。下面的查询对于类型A的行具有别名 A,对于类型B的行具有别名(同时仍使用相同的实际表DATA)。通过对A进行分组,您可以使用集合函数 min(B.TransSeq)来获取该客户的最低B序列。这样,您就可以获取所有在min(B)之前的A。

我打算提出第二种解决方案,并在子选择上加入联接,但这基本上就是Eugen Rieck提出的方案,因此我会坚持使用这一方案,并让您测试哪种方案最适合您的方案。总体思路是一样的。

我不知道这是比其他解决方案快还是慢。我认为此查询绝对可以从(CustId,Type)和/或(CustId,Type,TransId)的组合索引中受益。如果是这样,它可能会更高效,因为它先连接然后是组(以便更好地使用索引),或者效率可能更低,因为它必须使用更大的中间数据集。因此,这取决于各种因素,包括您拥有的索引,硬件配置以及是否要为一小部分客户或整个表查询此问题。

select
  A.*
from 
  DATA A
  inner join DATA B on B.custid =  A.custID and B.Type = 'B'
where 
  A.Type = 'A'
  -- and A.CustId = 100 -- if you like to filter by customer
group by
  A.TranId
having
  A.TransSeq < min(B.TransSeq);