我試圖檢測並默默丟棄任何重複的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)。
爲什麼不在Rails中創建唯一性驗證並在PG中添加複合索引以防止競爭條件? – max
做什麼最大的說。只強制記錄唯一記錄,如果記錄存在,則可以進行更新。 – bkunzi01
唯一性驗證在高容量情況下不是完全可靠的,因爲Rails基本上只是在INSERT之前運行SELECT來執行檢查。此外,我們有時會在數據庫上運行更新腳本,因此希望將支票存放在那裏,但不要在任何地方拋出異常。 – BeachBum