我有一个表格,其中包含许多其他字段hstore
分贝/ schema.rb
create_table "requests", force: true do |t|
t.hstore "parameters"
end
有些记录有一个字段parameters["company_id"]
但不是全部。
我需要做的是确保只使用给定的Request
创建一个parameters["company_id"]
对象。可能有多次尝试同时保存记录 - 因此竞争条件。
我在整个表格的company_id
内寻找唯一的hstore
值。
我发现我可以运行一个事务来锁定数据库并检查给定parameters["company_id"]
的请求是否存在,如果没有创建它。如果company_id
是Request
模型上的简单字段,我可以执行以下操作:
Request.transaction do
if Request.find_by(company_id: *id* )
log_duplication_attempt_and_quit
else
create_new_record
log_successful_creation
end
end
不幸的是hstore
我无法改变它。使用hstore
实现此目标的最佳方式是什么?
我正在寻找快速的东西,因为表中有很多记录。 纯SQL查询是可以的 - 遗憾的是我没有足够的SQL背景来解决我的问题。 这可以为性能编制索引吗?
示例:
a = Request.new(parameters: {company_id: 567, name: "John"})
b = Request.new(parameters: {name: "Doesn't have company_id in the hstore"})
c = Request.new(parameters: {company_id: 567, name: "Galt"})
a.save // valid success
b.save // valid success even if company_id hasn't been provided
c.save // not valid Request with company_id 567 already in the table
答案 0 :(得分:1)
即使使用普通列,您的想法也不会保存以防止并发访问。两个事务可能两者看到该值尚未同时存在且两者尝试插入。
显然,为此目的使用专用company_id
列会更清晰,然后一个简单的UNIQUE约束就能完成这项工作:
ALTER TABLE requests ADD CONSTRAINT requests_company_id_uni UNIQUE (company_id);
这样您就可以自动::
你甚至可以将该列作为外键引用......
使用设置 ,您仍然可以使用functional UNIQUE index:
CREATE UNIQUE INDEX requests_parameters_company_id_uni
ON requests ((parameters->'company_id')); -- all parentheses required
两种变体都允许多个NULL值,通常允许不带'company_id'
键的条目。您甚至可以将其设为partial, functional UNIQUE index以从索引中排除不相关的行(使索引更小):
CREATE UNIQUE INDEX requests_parameters_company_id_uni
ON requests ((parameters->'company_id'))
WHERE (parameters->'company_id') IS NOT NULL;
仅在没有company_id
的情况下才有用。
相关:
无论哪种方式,Postgres现在处理其余的事情。尝试插入已存在company_id
的行的任何事务(以这种方式或其他方式)将引发唯一违规的异常并回滚整个事务。始终保证唯一性。
如果要将作为重复项被拒绝的条目记录下来,可以将INSERT封装在服务器端函数中,请捕获唯一违规并写入日志表:
您将在SO with this search上找到示例。