我正在尝试检测并静默丢弃任何重复的 INSERT 到表中,如果检测到重复,则返回现有记录的 ID(主键)。否则,插入记录并返回新的 ID。
我可以使用 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 控制台或带有 INSERT 的 psql 中测试它工作正常。如果存在欺骗,它将返回第一个现有记录的 ID,而不执行 INSERT。否则,它将继续执行 INSERT。但是,在 Rails 中,它会失败并返回此错误:
错误:无法对关系“territory_products”执行插入返回 提示:您需要一个带有 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,因为 Return NULL(这是禁止 INSERT 所必需的)。
任何人都可以解决这些问题中的任何一个,以便我得到我正在寻找的结果吗? (例如,在发生重复的情况下静默丢弃 INSERT 并返回现有记录的 ID。或者如果 INSERT 成功,则返回新记录的 ID)。
最佳答案
为您的数据库添加唯一索引:
# 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
关于ruby-on-rails - Rails Postgres 插入重复行检查问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45513357/