MySQL --- 在同一个 table 上设置减号和相交操作

MySQL --- set operations MINUS and INTERSECT on the same table

我们需要跟踪随时间变化的集成员资格。我可以用以下简单的 table:

来解释我们的问题
+-----------+------------------+------+-----+---------+----------------+
| Field     | Type             | Null | Key | Default | Extra          |
+-----------+------------------+------+-----+---------+----------------+
| id        | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| timestamp | varchar(8)       | NO   |     | NULL    |                |
| member    | varchar(4)       | NO   |     | NULL    |                |
+-----------+------------------+------+-----+---------+----------------+

SELECT*运算结果如下:

SELECT * FROM My_Table;
+----+-----------+--------+
| id | timestamp | member |
+----+-----------+--------+
|  1 | 20150101  | A      |
|  2 | 20150101  | B      |
|  3 | 20150101  | C      |
|  4 | 20180101  | A      |
|  5 | 20180101  | D      |
|  6 | 20180101  | E      |
+----+-----------+--------+

从逻辑上讲,我们可以使用 SET 操作 MINUS 和 INTERSECT 来了解在一段时间内添加、删除或保留了哪些成员。例如,下面的 "logical" 集合操作给出了在 20150101 和 20180101 之间添加的成员数:

SELECT member FROM my_table WHERE timestamp = "20180101"
MINUS
SELECT member FROM my_table WHERE timestamp = "20150101";

同样,下面的"logical"设置操作给出了20150101到20180101之间掉线的成员数:

SELECT member FROM my_table WHERE timestamp = "20150101"
MINUS
SELECT member FROM my_table WHERE timestamp = "20180101";

下面的"logical"集合运算给出了20150101到20180101之间保留的成员数:

SELECT member FROM my_table WHERE timestamp = "20150101"
INTERSECT
SELECT member FROM my_table WHERE timestamp = "20180101";

在 MySQL 中实现这些逻辑集合操作的最优雅的方法是什么?

LEFT JOIN 最有可能满足您对前两个的需求,第三个是简单的 INNER JOIN。

已添加成员

SELECT t1.member 
FROM my_table AS t1
LEFT JOIN my_table AS t2 ON t1.member = t2.member AND t2.timestamp = "20150101"
WHERE t1.timestamp = "20180101" 
    AND t2.id IS NULL -- this filters results to only those members who did not have an entry in t2
;

会员流失 应该只能交换上面的时间戳值。

使用这种通用形式:

SELECT t1.member 
FROM my_table AS t1
LEFT JOIN my_table AS t2 ON t1.member = t2.member AND t2.timestamp = Y
WHERE t1.timestamp = X
    AND t2.id IS NULL -- this filters results to only those members who did not have an entry in t2
;

查询基本上是获取记录在时间戳 X 时存在但在时间戳 Y 时未记录为存在的成员。如果 X > Y,那将是成员获得;如果 X < Y. 那将是成员丢失。

保留成员

SELECT t1.member 
FROM my_table AS t1
INNER JOIN my_table AS t2 ON t1.member = t2.member 
   AND t2.timestamp = "20150101" -- This condition would be more "properly" 
                                 -- part of the WHERE clause's conditions, 
                                 -- but (unlike the LEFT JOIN queries) whether it 
                                 -- is in the ON or the WHERE will not change the results
WHERE t1.timestamp = "20180101"
;