繁体   English   中英

MySQL-仅在前一行字段为0时才选择行?

[英]MySQL - Select row only if previous row field was 0?

我有一张桌子,如下所示:

 CREATE TABLE IF NOT EXISTS `status` 
 (`code` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY
 ,`IMEI` varchar(15) NOT NULL
 ,`ACC` tinyint(1) NOT NULL
 ,`datetime` datetime NOT NULL
 );

 INSERT INTO status VALUES
 (1,    123456789012345,    0,    '2014-07-09 10:00:00'),
 (2,    453253453334445,    0,    '2014-07-09 10:05:00'),
 (3,    912841851252151,    0,    '2014-07-09 10:08:00'),
 (4,    123456789012345,    1,    '2014-07-09 10:10:00'),
 (5,    123456789012345,    1,    '2014-07-09 10:15:00');

我需要获取给定IMEI (eg 123456789012345)所有行,其中ACC = 1,而同一IMEI的上一行具有ACC = 0。 这些行可以一个接一个,也可以很分开。

鉴于上面的示例,我想获得第4行(代码4),而不是第5行(代码5)。

有任何想法吗? 谢谢。

假设您按日期时间表示上一行

SELECT * 
  FROM status s
 WHERE s.imei='123456789012345'
   AND s.acc=1
   AND (
  SELECT acc
    FROM status
   WHERE imei=s.imei
     AND datetime<s.datetime
ORDER BY datetime DESC
   LIMIT 1
       ) = 0

我处理此问题的方式与其他答案中给出的方式大不相同。

我将使用的方法是

1)首先按imei排序行,然后按每个imeidatetime排序。 (我假设datetime是确定行是否在另一行“之前”的方式。

2)顺序地处理的行中,第一比较imei从当前行到imei从以前的行,然后检查是否ACC从当前行是1ACC从上一行是0 然后,我知道当前行是要返回的行。

3)对于结果集中的每个已处理行,都包括一列,指示是否应返回该行

4)仅返回已设置指标列的行

查询如下:

SELECT t.code
     , t.imei
     , t.acc
     , t.datetime
  FROM ( SELECT IF(s.imei=@prev_imei AND s.acc=1 AND @prev_acc=0,1,0) AS ret
              , s.code                                                AS code
              , @prev_imei := s.imei                                  AS imei
              , @prev_acc  := s.acc                                   AS acc
              , s.datetime                                            AS datetime
           FROM (SELECT @prev_imei := NULL, @prev_acc := NULL) i
          CROSS
           JOIN `status` s
          WHERE s.imei = '123456789012345'
          ORDER BY s.imei, s.datetime, s.code
       ) t
 WHERE t.ret = 1

(我可以对其进行一些说明,以解释其工作原理。)

但是这种方法的最大缺点是,它要求MySQL将内联视图具体化为派生表(临时MyISAM表)。 如果status表上没有谓词(WHERE子句),则内联视图本质上将是整个status表的副本。 而在MySQL 5.5和更早版本中,该派生表将不会被索引。 因此,这可能会带来大型设备的性能问题。

在内联视图查询中包含谓词(例如WHERE s.imei = '123456789'来限制status表中的行)可能会充分限制临时MyISAM表的大小。

这种方法的另一个陷阱是不能保证语句中用户定义变量的行为。 但是,我们确实观察到了可以使用的一致行为。 它确实起作用,但是MySQL文档警告不能保证该行为。


这是MySQL如何处理此查询的粗略概述。

首先,MySQL对别名为i的内联视图运行查询。 我们真的不在乎此查询返回的内容,只是由于JOIN操作,我们需要它仅返回一行。 我们关心的是两个MySQL用户定义变量@prev_imei@prev_acc 稍后,我们将使用这些用户定义的变量来“保留”先前处理的行中的值,因此我们可以将这些值与当前行进行比较。

status表中的行根据ORDER BY子句顺序处理。 (这可能会在将来的发行版中更改,但我们可以观察到它在MySQL 5.1和5.5中的工作方式与此相同。)

对于每一行,我们将当前行中的imeiacc值与上一行中保留的值进行比较。 如果IF表达式中的布尔值计算为TRUE,则返回1,以指示应返回此行。 否则,我们返回0,以表示我们不想返回此行。 (对于处理的第一行,我们先前将用户定义的变量初始化为NULL,因此IF表达式的计算结果为0。)

@prev_imei := s.imei@prev_acc := s.acc将当前行中的值分配给用户定义的值,因此它们可用于处理的下一行。

请注意,在使用当前行中的值覆盖之前的值之前 ,对用户定义的变量(SELECT列表中的第一个表达式) 进行测试非常重要。

我们可以仅从内联视图t运行查询,以观察行为。

外部查询从内联视图返回将派生的ret列设置为1的行,而这些行我们要返回。

select * from status s1
WHERE 
ACC = 1
AND code = (SELECT MIN(CODE) FROM status WHERE acc = 1 and IMEI = s1.IMEI)
AND EXISTS (SELECT * FROM status WHERE IMEI = s1.IMEI AND ACC = 0)
AND IMEI = 123456789012345
SELECT b.code,b.imei,b.acc,b.datetime
  FROM 
     ( SELECT x.*
            , COUNT(*) rank 
         FROM status x
         JOIN status y
           ON y.imei = x.imei
          AND y.datetime <= x.datetime
        GROUP
           BY x.code
     ) a
  JOIN
     ( SELECT x.*
            , COUNT(*) rank 
         FROM status x
         JOIN status y
           ON y.imei = x.imei
          AND y.datetime <= x.datetime
        GROUP
           BY x.code
     ) b
    ON b.imei = a.imei
   AND b.rank = a.rank + 1
 WHERE b.acc = 1
   AND a.acc = 0;

您可以执行常规的IN() ,然后将所有重复项IMEI为一group (也可以使用limit但这仅适用于一个IMEI

设定:

INSERT INTO `status` 
VALUES
 (1,    123456789012345,    0,    '2014-07-09 10:00:00'),
 (2,    453253453334445,    0,    '2014-07-09 10:05:00'),
 (3,    912841851252151,    0,    '2014-07-09 10:08:00'),
 (4,    123456789012345,    1,    '2014-07-09 10:10:00'),
 (5,    123456789012345,    1,    '2014-07-09 10:15:00'),
 (6,    123456789012345,    1,    '2014-07-09 10:15:00'),
 (7,    453253453334445,    1,    '2014-07-09 10:15:00');

查询:

SELECT * FROM status
WHERE ACC = 1 AND IMEI IN(
    SELECT DISTINCT IMEI FROM status
    WHERE ACC = 0)
GROUP BY imei;

结果:

与多个0到1的IMEI一起使用... IMAGE


编辑:如果您也想按输入的日期,则可以先按日期排序,然后再分组。

SELECT * FROM(
    SELECT * FROM status
    WHERE ACC = 1 AND IMEI IN(
        SELECT DISTINCT IMEI FROM status
        WHERE ACC = 0)
    ORDER BY datetime
) AS t
GROUP BY imei;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM