2014-12-03 60 views
0

我有一個非常大的MySQL表(68萬行)排序指標,MySQL的非常大的表 - 創造超過24小時

,我嘗試使用下面的查詢,以保持每分鐘一列:

delete bt  
    from table1 bt 
    join   (select date, min(time) as time 
     from table1   
    group by date, hour(time), minute(time)   
    ) 
    btt   
    on btt.date = bt.date 
and hour(bt.time) = hour(btt.time) 
and minute(bt.time) = minute(btt.time) 
and btt.time <> bt.time 

我的表有查詢運行超過24小時以下的列

+----------------+-------------+------+-----+---------+----------------+ 
| Field   | Type  | Null | Key | Default | Extra   | 
+----------------+-------------+------+-----+---------+----------------+ 
| id    | int(11)  | NO | PRI | NULL | auto_increment | 
| date   | varchar(11) | NO |  | NULL |    | 
| time   | varchar(12) | NO |  | NULL |    | 
| gmt_offset  | varchar(2) | YES |  | NULL |    | 
| type   | varchar(10) | YES |  | NULL |    | 
| yield_b  | varchar(10) | YES |  | NULL |    | 
| yield_d  | varchar(10) | YES |  | NULL |    | 
+----------------+-------------+------+-----+---------+----------------+ 

,當我運行

SHOW FULL PROCESSLIST; 

國家說

Creating sort index 

這是正常的這樣一個查詢,以這麼長的時間?有什麼方法可以加快速度嗎?謝謝 !

編輯:

戈登的答案是正確的,只有一條線有一個小的失誤。下面是確實工作起來比前一個更快的正確的查詢:

create table temp_table1 as 
    select t.* 
    from (select t1.*, 
       (@rn := if(@prevd = date and minute(time) = @prevm, @rn + 1, 
          if(@prevd := date, if(@prevm := minute(time), 1, 1), 1) 
          ) 
       ) as seqnum 
      from table1 t1 cross join 
       (select @rn := 0, @prevd := 0, @prevm := 0) vars 
      order by date, time 
     ) t 
    where seqnum = 1; 
+0

請說明。從您的查詢看來,您正試圖刪除除了每個不同分鐘中的第一行(最小值「time」)行之外的所有行。那是對的嗎?請顯示您的索引。你有什麼理由必須分開'日期'和'時間'嗎?如果您將它們合併到一個列中,給它一個'DATETIME'數據類型並對它進行索引,那麼您就可以順利地找到一個有效的解決方案。 – 2014-12-03 14:11:38

+0

還有一個問題:id值是隨着日期+時間單調增加的嗎?也就是說,如果一個「id」大於另一個,那麼它是保證它的日期+時間與另一個同時或晚於另一個? – 2014-12-03 14:15:29

回答

1

而不是刪除了一堆行,創造你想要的數據一個臨時表,然後再截斷原始表並插入它放回:

create table temp_table1 as 
    select t.* 
    from (select t1.*, 
       (@rn := if(@prevd <> date or minute(time) <> @prevm, 1, 
          if(@prevd := date, if(@prevm := minute(time), 1, 1), 1) 
          ) 
       ) as seqnum 
      from table1 t1 cross join 
       (select @rn := 0, @prevd := 0, @prevm := 0) vars 
      order by date, time 
     ) t 
    where seqnum = 1; 


truncate table table1; 

insert into table1(col1, . . ., coln) 
    select col1, . . . , coln 
    from temp_table1; 

第一個查詢具有枚舉一分鐘內的行的子查詢。然後,只保留第一個。然後將其插入到表格的空白版本中。當然,在截斷原始表之前測試第一個查詢的結果(並且爲了以防萬一,我會將數據複製到其他地方)。

+0

太棒了。非常感謝!我也不需要插入seqnum,對吧?這個變量的含義是什麼?某種索引? – adrCoder 2014-12-03 16:18:59

+0

@adrCoder。 。 。 seqnum正在枚舉原始表中發生在同一分鐘內的行。邏輯採取第一個。 – 2014-12-04 01:26:26

+0

嗨戈登。您的解決方案不起作用。它基本上把seqnum = 1放到每一行,並且不會刪除任何行。你可以看看嗎?謝謝 – adrCoder 2015-01-19 12:23:13

2

戈登的答案很好。這是另一種方法,如果你的id隨着時間的推移單調增加,那麼這種方法就行得通。

首先,抓住每個不同分鐘中第一次觀察值的id值。

SELECT MIN(id) As first_id_in_minute 
    FROM table1 
GROUP BY date, HOUR(time), MINUTE(time) 

這些是您想要保留的行的id值。

然後刪除其餘的行。使用LEFT JOIN ... IS NULL來獲取不匹配的行。這可能比​​快。

DELETE a 
    FROM table1 AS a 
    LEFT JOIN (
        SELECT MIN(id) As first_id_in_minute 
        FROM table1 
       GROUP BY date, HOUR(time), MINUTE(time) 
      ) AS b ON a.id = b.first_id_in_minute 
WHERE b.first_id_in_minute IS NULL 
LIMIT 1000 

我把LIMIT 1000放在每個DELETE操作的大小。您應該重複此查詢,直到它聲明沒有行受到影響。

嘗試在(date, time, id)上放置複合索引以加速MIN() ... GROUP BY的這一部分。

像Gordon建議的那樣,試試這張表的副本,呃?

+0

嗨Ollie。事實上,在我的數據中,隨着ID的增加,時間越來越長,所以你所說的是一個非常好的主意。謝謝 – adrCoder 2014-12-03 16:19:31