簡體   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