我已经阅读了UPSERTPostgreSQL 中的不同实现,但所有这些解决方案都相对较旧或相对奇特(例如,使用可写 CTE)。
而且我根本不是 psql 专家,无法立即找出这些解决方案是否过时,因为它们被很好地推荐,或者它们(好吧,几乎所有这些都是)只是不适合生产使用的玩具示例。
在 PostgreSQL 中实现 UPSERT 的最线程安全的方法是什么?
我有一个表测试,其中包含主键和自动递增的列 id 和名称。当且仅当没有记录时,我想插入一条新记录。例如
输入是 id=30122 和 name =john
如果有 ID 为 30122 的记录,那么我将 name 列更新为 john,如果没有记录,那么我将插入一条新记录。
我可以使用 2 个查询,例如
select * from test where id=30122
Run Code Online (Sandbox Code Playgroud)
如果它有一些记录,那么我可以使用 update test set name='john' where id=3012
或者如果它没有记录,那么我可以使用
insert into test(name) values('john')
Run Code Online (Sandbox Code Playgroud)
但我想使用单个查询?
有人可以告诉它是否可能吗?
鉴于:
CREATE TABLE A (
PK_A INT8 NOT NULL,
A INT8,
PRIMARY KEY (PK_A)
);
CREATE TABLE B (
PK_B INT8 NOT NULL,
B INT8,
PRIMARY KEY (PK_B)
);
Run Code Online (Sandbox Code Playgroud)
这个查询:
insert into table_b (pk_b, b)
select pk_a,a from table_a
on conflict (b) do update set b=a;
Run Code Online (Sandbox Code Playgroud)
导致以下错误:
Run Code Online (Sandbox Code Playgroud)ERROR: column "a" does not exist LINE 1: ...elect pk_a,a from table_a on conflict (b) do update set b=a; ^ HINT: There is a column named "a" in table "*SELECT*", but it cannot …
我有一个tag包含 2 列的表:id(uuid) 和name(text)。我现在想在表中插入一个新标签,但如果该标签已经存在,我只想获取id现有记录的 。
我以为我可以ON CONFLICT DO NOTHING结合使用RETURNING "id":
INSERT INTO
"tag" ("name")
VALUES( 'foo' )
ON CONFLICT DO NOTHING
RETURNING "id";
Run Code Online (Sandbox Code Playgroud)
但是,如果名称为“foo”的标签已经存在,则会返回一个空的结果集。
然后我将查询更改为使用 noopDO UPDATE子句:
INSERT INTO
"tag" ("name")
VALUES( 'foo' )
ON CONFLICT ("name") DO UPDATE SET "name" = 'foo'
RETURNING "id";
Run Code Online (Sandbox Code Playgroud)
这按预期工作,但有点令人困惑,因为我只是将名称设置为已经存在的值。
这是解决这个问题的方法还是我缺少更简单的方法?
我使用 PostgreSQL 作为我的数据库。我需要在数据库中创建一个条目,如果它已经存在,只需更新其字段,但只有在未设置时才应更新其中一个字段。
我已经使用了这个问题的信息:https : //stackoverflow.com/questions/13305878/dont-update-column-if-update-value-is-null,它与我所拥有的非常相关。
我尝试使用此查询,但是当我运行它时,它会出错Column reference 'affiliate_code' is ambiguous:
INSERT INTO accounts (id, token, affiliate_code)
VALUES (value1, value2, value3)
ON CONFLICT (id) DO
UPDATE SET token = value2,
affiliate_code = COALESCE(affiliate_code, value3);
Run Code Online (Sandbox Code Playgroud)
(当然,实际值会被替换)。
如果我替换affiliate_code = COALESCE(affiliate_code, value3)为affiliate_code = value3,一切正常,但不是我想要的方式。
我怎样才能使这项工作?
这是我的表的定义方式:
CREATE TABLE accounts (
id VARCHAR NOT NULL UNIQUE,
token VARCHAR NOT NULL,
affiliate_code VARCHAR
);
Run Code Online (Sandbox Code Playgroud) 我在 Postgres 9.5 中使用新的 UPSERT 功能时遇到问题
我有一个表,用于从另一个表聚合数据。复合键由 20 列组成,其中 10 列可以为空。下面我创建了我遇到的问题的较小版本,特别是 NULL 值。
CREATE TABLE public.test_upsert (
upsert_id serial,
name character varying(32) NOT NULL,
status integer NOT NULL,
test_field text,
identifier character varying(255),
count integer,
CONSTRAINT upsert_id_pkey PRIMARY KEY (upsert_id),
CONSTRAINT test_upsert_name_status_test_field_key UNIQUE (name, status, test_field)
);
Run Code Online (Sandbox Code Playgroud)
根据需要运行此查询(首先插入,然后插入只会增加计数):
INSERT INTO test_upsert as tu(name,status,test_field,identifier, count)
VALUES ('shaun',1,'test value','ident', 1)
ON CONFLICT (name,status,test_field) DO UPDATE set count = tu.count + 1
where tu.name = 'shaun' AND tu.status = 1 AND tu.test_field = …Run Code Online (Sandbox Code Playgroud) 我在列a和b. 我需要这样的东西:
insert into my_table (a, b) values (1, 2), (1, 2)
on conflict (a) do update set c = 'a_violation'
on conflict (b) do update set c = 'b_violation'
Run Code Online (Sandbox Code Playgroud)
所以一般我想根据冲突目标进行不同的更新 - 不支持上面的语法(只支持一个on conflict语句)。有没有其他方法可以做到这一点?
不知道大家有没有遇到过类似UPSERT这个概念的T-SQL命令?使用选项 (1) 或 (2) 执行 INSERT|UPDATE 操作似乎过于复杂且容易出错。
客观的
确保所需的记录(在本例中为 employee_id 1)是最新的,而不必实质上编写相同的查询两次。
语境
选项
执行 SQL UPDATE... 检查 @@rowcount = 0 和 @@error = 0 ... 如果需要,执行 SQL INSERT
/sf/ask/77470221/ "Update using @@rowcount"
我有一个 Web 服务(http api),它允许用户安静地创建资源。在身份验证和验证之后,我将数据传递给 Postgres 函数,并允许它检查授权并在数据库中创建记录。
我今天发现了一个错误,即在同一秒内发出了两个 http 请求,导致使用相同的数据两次调用此函数。函数内部有一个子句,它在表上进行选择以查看值是否存在,如果存在,则获取 ID 并在下一个操作中使用该 ID,如果不存在,则插入数据,获取返回 ID,然后在下一个操作中使用它。下面是一个简单的例子。
select id into articleId from articles where title = 'my new blog';
if articleId is null then
insert into articles (title, content) values (_title, _content)
returning id into articleId;
end if;
-- Continue, using articleId to represent the article for next operations...
Run Code Online (Sandbox Code Playgroud)
正如您可能猜到的那样,我对数据进行了幻读,其中两个事务都进入了if articleId is null then块并试图插入到表中。一个成功了,另一个失败了,因为一个领域的独特限制。
我已经环顾四周,看看如何抵御这种情况,并找到了一些不同的选择,但由于某些原因,它们似乎都不适合我们的需求,我正在努力寻找任何替代方案。
insert ... on conflict do nothing/update...我首先查看了on conflict看起来不错的选项,但是唯一的选项是do nothing不返回导致冲突的记录的 ID,并且do update不会工作,因为它会导致触发器在实际数据时被触发没有改变。在某些情况下,这不是问题,但在许多情况下,这可能会使会话用户会话无效,这是我们无法做到的。 …我正在尝试使用一组值更新表。数组中的每一项都包含与 SQL Server 数据库表中的行匹配的信息。如果该行已存在于表中,我们将使用给定数组中的信息更新该行。否则,我们在表中插入一个新行。我已经基本上描述了 upsert。
现在,我试图在采用 XML 参数的存储过程中实现这一点。我使用 XML 而不是表值参数的原因是,执行后者时,我必须在 SQL 中创建自定义类型并将此类型与存储过程相关联。如果我以后更改了存储过程或数据库架构中的某些内容,则必须重做存储过程和自定义类型。我想避免这种情况。此外,TVP 相对于 XML 的优势对我的情况没有用,因为我的数据数组大小永远不会超过 1000。这意味着我不能使用这里提出的解决方案:How to insert multiple records using XML in SQL server 2008
此外,这里的类似讨论(UPSERT - 是否有更好的替代 MERGE 或 @@rowcount?)与我所问的不同,因为我试图将多行插入到表中。
我希望我可以简单地使用以下一组查询来更新 xml 中的值。但这行不通。当输入是单行时,这种方法应该有效。
begin tran
update table with (serializable) set select * from xml_param
where key = @key
if @@rowcount = 0
begin
insert table (key, ...) values (@key,..)
end
commit tran
Run Code Online (Sandbox Code Playgroud)
下一个替代方法是使用详尽的 IF EXISTS 或其以下形式的变体之一。但是,我以次优效率为由拒绝了这一点:
IF (SELECT COUNT ... ) > 0 …Run Code Online (Sandbox Code Playgroud) upsert ×10
postgresql ×7
plpgsql ×2
sql-server ×2
coalesce ×1
concurrency ×1
duplication ×1
merge ×1
null ×1
t-sql ×1
transaction ×1
xml ×1