我有一张表格如下:
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)。
有什么想法吗?感谢。
答案 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}}和前一行的1
为ACC
。然后我会知道当前行是要返回的行。
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中的工作原理如此。)
对于每一行,我们将当前行的status
和imei
值与前一行保留的值进行比较。如果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;