Dus*_*att 14 sql django postgresql upsert sql-returning
我有这个表(由Django生成):
CREATE TABLE feeds_person (
id serial PRIMARY KEY,
created timestamp with time zone NOT NULL,
modified timestamp with time zone NOT NULL,
name character varying(4000) NOT NULL,
url character varying(1000) NOT NULL,
email character varying(254) NOT NULL,
CONSTRAINT feeds_person_name_ad8c7469_uniq UNIQUE (name, url, email)
);
Run Code Online (Sandbox Code Playgroud)
我想批量插入大量使用数据INSERT与ON CONFLICT条款.
皱纹是我需要得到所有行的id背面,无论它们是否已经存在.
在其他情况下,我会做类似的事情:
INSERT INTO feeds_person (created, modified, name, url, email)
VALUES blah blah blah
ON CONFLICT (name, url, email) DO UPDATE SET url = feeds_person.url
RETURNING id
Run Code Online (Sandbox Code Playgroud)
执行该操作UPDATE会导致该语句返回该id行.除此之外,它不适用于此表.我认为它不起作用,因为我有多个独特的字段,而在其他情况下,我使用这种方法,我只有一个独特的字段.
尝试通过Django的游标运行SQL时出现此错误:
Run Code Online (Sandbox Code Playgroud)django.db.utils.ProgrammingError: ON CONFLICT DO UPDATE command cannot affect row a second time HINT: Ensure that no rows proposed for insertion within the same command have duplicate constrained values.
如何使用此表进行批量插入并取回插入的和现有的ID?
Erw*_*ter 27
你得到的错误:
ON CONFLICT DO UPDATE命令不能再次影响行
表示您尝试在单个命令中多次突发同一行.换句话说:你(name, url, email)的VALUES列表中有dupes .折叠重复(如果这是一个选项),它应该工作.但是你必须决定从每组欺骗中选择哪一行.
INSERT INTO feeds_person (created, modified, name, url, email)
SELECT DISTINCT ON (name, url, email) *
FROM (
VALUES
('blah', 'blah', 'blah', 'blah', 'blah')
-- ... more
) v(created, modified, name, url, email) -- match column list
ON CONFLICT (name, url, email) DO UPDATE
SET url = feeds_person.url
RETURNING id;
Run Code Online (Sandbox Code Playgroud)
由于我们现在使用独立的VALUES表达式,您必须为非默认类型添加显式类型转换.喜欢:
VALUES
(timestamptz '2016-03-12 02:47:56+01'
, timestamptz '2016-03-12 02:47:56+01'
, 'n3', 'u3', 'e3')
...
Run Code Online (Sandbox Code Playgroud)
您的timestamptz列需要显式类型转换,而字符串类型可以默认操作text.(你仍然可以立即施放varchar(n).)
有几种方法可以确定从每组欺骗中选择哪一行:
你说得对,(目前)没有办法在条款中排除排除的行RETURNING.我引用Postgres Wiki:
请注意,
RETURNING不会使可见"EXCLUDED.*"从别名UPDATE(只是一般的"TARGET.*"的别名是可见的存在).这样做被认为很简单,通常情况下创建恼人的不确定性[30]对于几乎没有任何好处.在未来的某一时刻,我们可能会调查是否有暴露的方式RETURNING-projected元组插入和更新,但这可能并不需要,使之成为该功能的第一个关键迭代[31] .
但是,您不应该更新不应更新的行.空更新几乎与常规更新一样昂贵 - 并且可能会产生意想不到的副作用.你不一定要开始使用UPSERT,你的情况看起来更像是"SELECT或INSERT".有关:
插入一组行的一种更简洁的方法是使用数据修改CTE:
WITH val AS (
SELECT DISTINCT ON (name, url, email) *
FROM (
VALUES
(timestamptz '2016-1-1 0:0+1', timestamptz '2016-1-1 0:0+1', 'n', 'u', 'e')
, ('2016-03-12 02:47:56+01', '2016-03-12 02:47:56+01', 'n1', 'u3', 'e3')
-- more (type cast only needed in 1st row)
) v(created, modified, name, url, email)
)
, ins AS (
INSERT INTO feeds_person (created, modified, name, url, email)
SELECT created, modified, name, url, email FROM val
ON CONFLICT (name, url, email) DO NOTHING
RETURNING id, name, url, email
)
SELECT 'inserted' AS how, id FROM ins -- inserted
UNION ALL
SELECT 'selected' AS how, f.id -- not inserted
FROM val v
JOIN feeds_person f USING (name, url, email);
Run Code Online (Sandbox Code Playgroud)
增加的复杂性应该支付大表,INSERT规则和SELECT例外.
最初,我NOT EXISTS在最后添加了一个谓词,SELECT以防止结果中出现重复.但那是多余的.单个查询的所有CTE都会看到表的相同快照.返回ON CONFLICT (name, url, email) DO NOTHING的集与INNER JOIN在相同列之后返回的集互斥.
不幸的是,这也为比赛状况打开了一扇小窗口.如果...
......某些行可能会丢失.
您可能只是INSERT .. ON CONFLICT DO NOTHING,然后SELECT在同一事务中对所有行进行单独查询以克服此问题.这反过来又打开另一个小窗户的竞争条件,如果并发事务可以承诺之间写入表INSERT和SELECT(在默认的READ COMMITTED隔离级别).REPEATABLE READ事务隔离(或更严格)可以避免.或者在整个桌子上使用(可能是昂贵的甚至是不可接受的)写锁定.您可以获得所需的任何行为,但可能需要付出代价.
有关:
| 归档时间: |
|
| 查看次数: |
12913 次 |
| 最近记录: |