MAX有条件

时间:2014-10-17 09:58:59

标签: mysql

我有这个表代表人群中的成员身份:

+----+-----------+----------+------------+------------+
| id | person_id | group_id | from       | to         |
+----+-----------+----------+------------+------------+
| 1  | 1         | 1        | 2014-10-13 | 2014-10-20 |
+----+-----------+----------+------------+------------+
| 2  | 1         | 1        | 2014-10-17 | 2014-10-31 |
+----+-----------+----------+------------+------------+
| 3  | 1         | 1        | 2014-10-01 | 2014-10-15 |
+----+-----------+----------+------------+------------+
| 4  | 1         | 2        | 2014-11-01 | 2014-12-01 |
+----+-----------+----------+------------+------------+

我想为每个群组和个人选择合并的成员资格,并显示当前是否有效的状态。如果当前与“活动”成员资格重叠的“非活动”成员资格不会在结果中合并,那就没关系了(尽管如果可能的话也会很好)。今天是2014-10-17,所以在这种情况下的结果集应该是:

+-----------+----------+------------+------------+----------+
| person_id | group_id | from       | until      | status   |
+-----------+----------+------------+------------+----------+
| 1         | 1        | 2014-10-13 | 2014-10-31 | ACTIVE   |
+-----------+----------+------------+------------+----------+
| 1         | 2        | NULL       | NULL       | INACTIVE |
+-----------+----------+------------+------------+----------+

因此,对于第1组,使用的值是第1行from,第2行until,第3行被排除,即使其untilfrom重叠第1行。我更喜欢使用第3行中的from代替,但如果结果集如上所示则没问题。第2组处于非活动状态,因为它没有任何行from<现在()和until> NOW()。

现在我有这个:

CREATE TEMPORARY TABLE combinedRows ENGINE = MEMORY
SELECT
    `person_id`,
    `group_id`,
    MIN(`from`) AS `from`,
    MAX(`until`) AS `until`,
    'ACTIVE' AS `status`
FROM
    `memberships`
WHERE
    `person_id` = @updated_person
    AND `group_id` = @updated_bgroup
    AND `from` < NOW()
    AND `until` > NOW()
GROUP BY
    `person_id`,
    `group_id`;

其次是INSERT IGNORE INTO combinedRows SELECT inversed ...基本上我想在一个查询中做同样的事情(出于性能原因)。相当于这个“伪代码”的东西:

CREATE TEMPORARY TABLE combinedRows ENGINE = MEMORY
SELECT
    `person_id`,
    `group_id`,
    MIN(`from` WHERE `from` < NOW() and `until` > NOW()) DEFAULT NULL AS `from`,
    MAX(`until` WHERE `from` < NOW() and `until` > NOW()) DEFAULT NULL AS `until`,
    IF(*something*, 'ACTIVE', 'INACTIVE') AS `status`
FROM
    `memberships`
WHERE
    `person_id` = @updated_person
    AND `group_id` = @updated_bgroup
GROUP BY
    `person_id`,
    `group_id`;

这可能吗?或者我应该忘记它并且不担心性能?或者我应该以某种方式更改数据库设计?

1 个答案:

答案 0 :(得分:2)

SELECT
  `person_id`,
  `group_id`,
  MIN(CASE WHEN `from` < NOW() and `until` > NOW() THEN `from` ELSE NULL END) AS `from`,
  MAX(CASE WHEN `from` < NOW() and `until` > NOW() THEN `until` ELSE NULL END) AS `until`,
  MIN(CASE WHEN `from` < NOW() and `until` > NOW() THEN 'ACTIVE' ELSE 'INACTIVE' END) AS `status`
FROM
  `memberships`
WHERE
  `person_id` = @updated_person
  AND `group_id` = @updated_bgroup
GROUP BY
  `person_id`,
  `group_id`;

说明:

您可以将任何表达式放在聚合函数中(MAXMIN,...)。 NULL值被忽略,MIN(或MAX)的值是根据NULL的值计算的。

如果你看一下:

MIN(CASE WHEN `from` < NOW() and `until` > NOW() THEN 'ACTIVE' ELSE 'INACTIVE' END)

那么这里的技巧是:如果有满足条件的行,则返回'ACTIVE',因为'ACTIVE'在作为字符串进行比较时小于'INACTIVE'。