[英]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
排序行,然后按每個imei
的datetime
排序。 (我假設datetime
是確定行是否在另一行“之前”的方式。
2)順序地處理的行中,第一比較imei
從當前行到imei
從以前的行,然后檢查是否ACC
從當前行是1
和ACC
從上一行是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中的工作方式與此相同。)
對於每一行,我們將當前行中的imei
和acc
值與上一行中保留的值進行比較。 如果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.