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

时间:2014-07-09 14:58:51

标签: mysql

我有一张表格如下:

 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 (e.g 123456789012345)的所有行,其中ACC = 1并且相同IMEI的前一行具有ACC = 0。行可以是一个接一个或非常分开。

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

有什么想法吗?感谢。

5 个答案:

答案 0 :(得分:2)

假设您的意思是前一行按日期时间

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 :(得分:2)

我接触这个问题的方式与其他答案中给出的方法大不相同。

我将使用的方法是

1)首先按imei排序,然后在每个datetime内按imei排序。 (我假设datetime是你如何确定一行是&#34;之前&#34;到另一行。

2)顺序处理行,首先将当前行的imei与前一行的imei进行比较,然后检查当前行的ACC是否为{{ 1}}和前一行的1ACC。然后我会知道当前行是要返回的行。

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

4)仅返回具有指标列集

的行

类似这样的查询:

0

(我可以解压一下,解释它是如何工作的。)

但这种方法的一大缺点是它需要MySQL将内联视图实现为派生表(临时MyISAM表)。如果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 表上没有谓词(WHERE子句),则内联视图基本上是整个status表的副本。使用MySQL 5.5及更早版本,派生表不会被编入索引。因此,这可能会给大型集合带来性能问题。

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

这种方法的另一个问题是语句中用户定义变量的行为保证。但我们确实观察到了一致的行为,我们可以利用它;它确实有效,但MySQL文档警告该行为保证。


这里概述了MySQL如何处理此查询。

首先,MySQL运行查询,将内联视图别名为status。我们并不关心这个查询返回的内容,除非我们需要它返回一行,因为JOIN操作。我们关心的是两个MySQL用户定义变量的初始化, i @prev_imei 。稍后,我们将使用这些用户定义的变量来保存&#34;来自先前处理的行的值,因此我们可以将这些值与当前行进行比较。

根据ORDER BY子句,@prev_acc表中的行按顺序处理。 (这可能会在将来的某个版本中发生变化,但我们可以发现它在MySQL 5.1和5.5中的工作原理如此。)

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

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

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

我们只能从内联视图@prev_acc := s.acc运行查询,以观察行为。

外部查询返回内联视图中的行,这些行的派生t列设置为1,我们想要返回的行。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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;

答案 4 :(得分:0)

您可以定期IN()然后group重复一次(您也可以使用limit,但这只适用于一个IMEI

<强> SETUP:

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');

<强> QUERY:

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

<强>结果:

适用于多个IMEI,其中包含0,然后是1 ... 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;