2014-01-22 44 views
1

我會先描述我的情況,以便儘可能清楚地說明下列問題。如何避免將重複項插入數據庫最有效的方法?

爲了簡單起見,讓我們說我有一個表中的MySQL數據庫(InnoDB的)有關於狗與結構記錄如下:

dog_id (PK) | dog_name 

並且在表10,000,000行(每個代表一種獨特的狗)並在dog_name列上構建索引。

我的程序搜索需要處理的獸醫記錄。每條記錄都以某種方式與一條狗相連,每條狗有100條記錄。我想找到還沒有插入數據庫的狗。

這意味着連續100次正在處理的記錄可以是關於已經在數據庫中的狗,因此狗不必被添加到數據庫中。但有時候會發生(如前面提到的1:100比例),我需要向數據庫添加一條狗,因爲這是程序第一次接近關於狗的記錄。 (我希望這個例子使我的情況清楚)

我的問題是: 如何驗證狗沒有被插入到數據庫中的最有效的方式是什麼?

  1. 將所有狗的名字(假設世界上所有的狗都擁有唯一的名字)加載到程序的內存中(一組),並檢查狗是否在集合中。當它在集合中時,我跳過記錄,當它不在時,我插入狗。
  2. 將該列定義爲UNIQUE並嘗試插入所有記錄。當由於唯一性而導致數據庫錯誤時,我只需跳過該狗並繼續。
  3. 查詢數據庫以確定每次處理記錄時狗是否在數據庫中,如果它在數據庫中,我將跳過記錄,如果不是,則將狗插入表中。

給你儘可能多的信息,我可以。我使用Python,SqlAlchemy,MySQL,InnoDB。

回答

1

您應該使用dog_name作爲主鍵,然後用

INSERT INTO dogs (dog_name) VALUES ('[NAME HERE]') ON DUPLICATE KEY UPDATE dog_name='[NAME HERE]'; 

這隻會插入唯一的狗的名字。如果您仍然想爲每隻狗使用數字ID,您可以將該列設置爲自動增量,但主鍵應該是狗的名稱(假設它們都是唯一的)。

SQLAlchemy沒有內置此功能but can make force it to make a similar query with session.merge()

+0

我之前還不知道「ON DUPLICATE KEY」語法。謝謝。不過,我想「INSERT IGNORE INTO」對我來說會很有用。問題是,不會更快地SELECT + INSERT?考慮比率100:1(只有SELECT和SELECT後跟INSERT) – Marek

+1

我不會推薦使用INSERT IGNORE,因爲這會忽略查詢中的任何錯誤,包括重複鍵。做一個'SELECT'然後一個'INSERT'會混合一些Python到SQL中,它並沒有被優化,並且可能比只執行INSERT ... ON DUPLICATE KEY' INSERT IGNORE'(純SQL) 。所以我建議使用INSERT ... ON DUPLICATE KEY。 – sundance

1

類似選項2或選項3的效果最好;他們應該花費相似的時間,哪一個會贏得,這取決於MySQL/InnoDB究竟是如何決定發生衝突的。我其實不知道;使用UNIQUE鍵插入可能觸發與SELECT相同的操作。原型和配置文件的性能。

如果性能問題,您可以始終手動編寫SELECT語句,因爲它相對簡單。這削減了Python的MySQL開銷來構造SQL;這通常不是一個大問題,但SQLAlchemy可以添加幾十層函數調用,以支持構建任意查詢的能力。您可以使用Python字符串格式將這些調用短路。

假設「S」是你的SQLAlchemy會話對象:

def dog_in_db(dog_name): 
    q = 'SELECT COUNT (*) FROM dogs WHERE dog_name = %s;' % dog_name 
    res = s.execute(q) 
    return res.first()[0] > 0 

你也可以嘗試選擇並檢查是否返回任何行:

q 'SELECT dog_id FROM dogs WHERE dog_name = %s;' % dog_name 
    res = s.execute(q) 
    return res.rowcount() > 0 

假設你的選項1是指裝載數據庫中的所有名字都會很慢。 MySQL將始終以比Python更快的速度執行它支持的任何單個操作;你在這裏做的是完全一樣的單一操作(在列表中找到一個成員)。

+0

謝謝。只有兩件事:1)你可以更具體 - 你的意思是「_hard-code SELECT語句_」? 2)在對你提出的想法進一步研究之後,我發現性能最好的'SELECT'應該是'SELECT COUNT(1)FROM dogs WHERE dog_name =%s LIMIT 1'。你怎麼看待這件事? – Marek

+0

我想我錯了:我的意思是「手寫代碼」。聽起來像你處理它優雅。 這聽起來像是一個非常合理的SQL。我猜測'LIMIT 1'位加速了SQLAlchemy Result解析。聽起來像'COUNT(1)'與COUNT(*)'相同,至少對於MySQL: http://stackoverflow.com/questions/1221559/count-vs-count1 –