简体   繁体   English

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

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

I have a table as below: 我有一张桌子,如下所示:

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

I need to get all rows for a given IMEI (eg 123456789012345) where ACC=1 AND the previous row for same IMEI has ACC=0. 我需要获取给定IMEI (eg 123456789012345)所有行,其中ACC = 1,而同一IMEI的上一行具有ACC = 0。 The rows may be one after the other or very apart. 这些行可以一个接一个,也可以很分开。

Given the exampl above, I'd want to get the 4th row (code 4) but not 5th (code 5). 鉴于上面的示例,我想获得第4行(代码4),而不是第5行(代码5)。

Any ideas? 有任何想法吗? Thanks. 谢谢。

Assuming that you mean previous row by datetime 假设您按日期时间表示上一行

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

The way I would approach this problem is much different from the approaches given in other answers. 我处理此问题的方式与其他答案中给出的方式大不相同。

The approach I would use would be to 我将使用的方法是

1) order the rows, first by imei , and then by datetime within each imei . 1)首先按imei排序行,然后按每个imeidatetime排序。 (I'm assuming that datetime is how you are going to determine if a row is "previous" to another row. (我假设datetime是确定行是否在另一行“之前”的方式。

2) sequentially process the rows, first comparing imei from the current row to the imei from the previous row, and then checking if the ACC from the current row is 1 and the ACC from the previous row is 0 . 2)顺序地处理的行中,第一比较imei从当前行到imei从以前的行,然后检查是否ACC从当前行是1ACC从上一行是0 Then I would know that the current row was a row to be returned. 然后,我知道当前行是要返回的行。

3) for each processed row, in the resultset, include a column that indicates whether the row should be returned or not 3)对于结果集中的每个已处理行,都包括一列,指示是否应返回该行

4) return only the rows that have the indicator column set 4)仅返回已设置指标列的行

A query something like this: 查询如下:

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

(I can unpack that a bit, to explain how it works.) (我可以对其进行一些说明,以解释其工作原理。)

But the big drawback of this approach is that it requires MySQL to materialize the inline view as a derived table (temporary MyISAM table). 但是这种方法的最大缺点是,它要求MySQL将内联视图具体化为派生表(临时MyISAM表)。 If there was no predicate (WHERE clause) on the status table, the inline view would essentially be a copy of the entire status table. 如果status表上没有谓词(WHERE子句),则内联视图本质上将是整个status表的副本。 And with MySQL 5.5 and earlier, that derived table won't be indexed. 而在MySQL 5.5和更早版本中,该派生表将不会被索引。 So, this could present a performance issue for large sets. 因此,这可能会带来大型设备的性能问题。

Including predicates (eg WHERE s.imei = '123456789' to limit rows from the status table in the inline view query may sufficiently limit the size of the temporary MyISAM table. 在内联视图查询中包含谓词(例如WHERE s.imei = '123456789'来限制status表中的行)可能会充分限制临时MyISAM表的大小。

The other gotcha with this approach is that the behavior of user-defined variables in the statement is not guaranteed. 这种方法的另一个陷阱是不能保证语句中用户定义变量的行为。 But we do observe a consistent behavior, which we can make use of; 但是,我们确实观察到了可以使用的一致行为。 it does work, but the MySQL documentation warns that the behavior is not guaranteed. 它确实起作用,但是MySQL文档警告不能保证该行为。


Here's a rough overview of how MySQL processes this query. 这是MySQL如何处理此查询的粗略概述。

First, MySQL runs the query for the inline view aliased as i . 首先,MySQL对别名为i的内联视图运行查询。 We don't really care what this query returns, except that we need it to return exactly one row, because of the JOIN operation. 我们真的不在乎此查询返回的内容,只是由于JOIN操作,我们需要它仅返回一行。 What we care about is the initialization of the two MySQL user-defined variables, @prev_imei and @prev_acc . 我们关心的是两个MySQL用户定义变量@prev_imei@prev_acc Later, we are going to use these user-defined variables to "preserve" the values from the previously processed row, so we can compare those values to the current row. 稍后,我们将使用这些用户定义的变量来“保留”先前处理的行中的值,因此我们可以将这些值与当前行进行比较。

The rows from the status table are processed in sequence, according to the ORDER BY clause. status表中的行根据ORDER BY子句顺序处理。 (This may change in some future release, but we can observe that it works like this in MySQL 5.1 and 5.5.) (这可能会在将来的发行版中更改,但我们可以观察到它在MySQL 5.1和5.5中的工作方式与此相同。)

For each row, we compare the values of imei and acc from the current row to the values preserved from the previous row. 对于每一行,我们将当前行中的imeiacc值与上一行中保留的值进行比较。 If the boolean in the IF expression evaluates to TRUE, we return a 1, to indicate that this row should be returned. 如果IF表达式中的布尔值计算为TRUE,则返回1,以指示应返回此行。 Otherwise, we return a 0, to indicate that we don't want to return this row. 否则,我们返回0,以表示我们不想返回此行。 (For the first row processed, we previously initialized the user-defined variables to NULL, so the IF expression will evaluate to 0.) (对于处理的第一行,我们先前将用户定义的变量初始化为NULL,因此IF表达式的计算结果为0。)

The @prev_imei := s.imei and @prev_acc := s.acc assigns the values from the current row to the user-defined values, so they will be available for the next row processed. @prev_imei := s.imei@prev_acc := s.acc将当前行中的值分配给用户定义的值,因此它们可用于处理的下一行。

Note that it's important that the tests of the user-defined variables (the first expression in the SELECT list) before we overwrite the previous values with the values from the current row. 请注意,在使用当前行中的值覆盖之前的值之前 ,对用户定义的变量(SELECT列表中的第一个表达式) 进行测试非常重要。

We can run just the query from the inline view t , to observe the behavior. 我们可以仅从内联视图t运行查询,以观察行为。

The outer query returns rows from the inline view that have the derived ret column set to a 1, rows that we wanted to return. 外部查询从内联视图返回将派生的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;

you can do a regular IN() and then group any duplicates (you could also use a limit but that would only work for one IMEI ) 您可以执行常规的IN() ,然后将所有重复项IMEI为一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;

RESULTS: 结果:

works with multiple IMEI that have a 0 then a 1... IMAGE 与多个0到1的IMEI一起使用... IMAGE


EDIT: if you would like to go by the date entered as well then you can just order it first by date and then group. 编辑:如果您也想按输入的日期,则可以先按日期排序,然后再分组。

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