2017-08-04 61 views
0

我試圖檢測並默默丟棄任何重複的INSERT到表中,如果檢測到重複,返回現有記錄的ID(主鍵)。否則,插入記錄並返回新的ID。Rails Postgres插入重複行檢查問題

我可以用RULE或TRIGGER來做到這一點,但兩者都有缺點。這裏是我的規則的一個例子:

CREATE OR REPLACE RULE territory_products_ignore_duplicate_inserts AS 
ON INSERT TO territory_products 
WHERE (EXISTS (SELECT 1 
     FROM territory_products tp 
     WHERE tp.territory_id = NEW.territory_id AND tp.product_id = NEW.product_id)) DO INSTEAD SELECT tp.id 
     FROM territory_products tp WHERE tp.territory_id = NEW.territory_id AND tp.product_id = NEW.product_id LIMIT 1; 

在SQL控制檯或psql中使用INSERT測試這個工作正常。如果存在重複,它會返回第一個現有記錄的ID,而不是INSERT。否則,它將繼續使用INSERT。但是,在Rails中,它失敗並返回此錯誤:

錯誤:無法對關係「territory_products」執行INSERT RETURNING提示:您需要使用RETURNING子句的無條件ON INSERT DO INSTEAD規則。

移動到一個觸發器,我試試這個:

CREATE OR REPLACE Function territory_products_ignore_dups() Returns Trigger 
As $$ 
Begin 
If Exists (
    Select id From territory_products tp 
    Where tp.territory_id = NEW.territory_id And tp.product_id = NEW.product_id    
) Then 
    Return NULL; 
End If; 
Return NEW; 
End; 
$$ Language plpgsql; 

Create Trigger territory_products_ignore_dups 
Before Insert On territory_products 
For Each Row 
Execute Procedure territory_products_ignore_dups(); 

這也能正常工作,但我不能讓它返回現有的ID,因爲返回NULL(這是需要禁止的INSERT)。

任何人都可以解決這些問題之一,所以我得到我期待的結果? (例如在愚蠢的情況下靜靜丟棄INSERT並返回現有記錄的ID,或者如果INSERT成功,則返回新記錄的ID)。

+4

爲什麼不在Rails中創建唯一性驗證並在PG中添加複合索引以防止競爭條件? – max

+0

做什麼最大的說。只強制記錄唯一記錄,如果記錄存在,則可以進行更新。 – bkunzi01

+0

唯一性驗證在高容量情況下不是完全可靠的,因爲Rails基本上只是在INSERT之前運行SELECT來執行檢查。此外,我們有時會在數據庫上運行更新腳本,因此希望將支票存放在那裏,但不要在任何地方拋出異常。 – BeachBum

回答

0

添加一個唯一索引數據庫:

# migration file 
add_index : territory_products, [:territory_id, :product_id], unique: true 

在你的邏輯查找現有記錄或創建如果它不存在。如果出現競賽狀況,它會引發異常。只需再次搜索記錄。

#logic when finding/creating record 
begin 
    TerritoryProduct.where(territory_id: params[:territory_id], product_id: params[:product_id]).first_or_create! 
rescue ActiveRecord::RecordNotUnique 
    # In case it already exists 
    TerritoryProduct.where(territory_id: params[:territory_id], product_id: params[:product_id]).first 
end 
+0

好的,我會買的。但我試圖避免引發異常,無論是在Rails中,還是在腳本中插入數據庫。只要忽略重複項並返回現有的ID即可。有沒有辦法讓規則或觸發器做到這一點? – BeachBum