在不存在PostgresQL的地方插入多行

时间:2014-07-15 22:21:37

标签: postgresql postgresql-9.2

我希望生成一个单独的SQL查询来批量插入表格中不存在的一系列行。我当前的设置为每个记录插入创建了一个新查询,类似于WHERE NOT EXISTS in PostgreSQL gives syntax error中详细介绍的解决方案,但我想将其移至单个查询以优化性能,因为我当前的设置可能会生成数百个查询一时间现在我正尝试下面添加的示例:

INSERT INTO users (first_name, last_name, uid) 
SELECT ( 'John', 'Doe', '3sldkjfksjd'), ( 'Jane', 'Doe', 'adslkejkdsjfds')
WHERE NOT EXISTS (
  SELECT * FROM users WHERE uid IN ('3sldkjfksjd', 'adslkejkdsjfds')
)

Postgres返回以下错误:

PG::Error: ERROR:  INSERT has more target columns than expressions

问题是PostgresQL似乎不想在使用SELECT时获取一系列值。相反,我可以使用VALUES进行插入,但是我无法使用WHERE NOT EXISTS生成重复项。

http://www.techonthenet.com/postgresql/insert.php在“示例 - 使用SUB-SELECT”一节中建议,使用SELECT可以从另一个引用的表中插入多个记录 ,因此我想知道为什么我可以&#39 ; t似乎传递了一系列值来插入。我传递的值来自外部API,因此我需要生成要手动插入的值。

2 个答案:

答案 0 :(得分:23)

您的select没有按照您的想法行事。

PostgreSQL中最紧凑的版本是这样的:

with data(first_name, last_name, uid)  as (
   values
      ( 'John', 'Doe', '3sldkjfksjd'),
      ( 'Jane', 'Doe', 'adslkejkdsjfds')
) 
insert into users (first_name, last_name, uid) 
select d.first_name, d.last_name, d.uid
from data d
where not exists (select 1
                  from users u2
                  where u2.uid = d.uid);

这几乎相当于:

insert into users (first_name, last_name, uid) 
select d.first_name, d.last_name, d.uid
from (
   select 'John' as first_name, 'Doe' as last_name, '3sldkjfksjd' as uid
   union all
   select 'Jane', 'Doe', 'adslkejkdsjfds'
) as d
where not exists (select 1
                  from users u2
                  where u2.uid = d.uid);

答案 1 :(得分:2)

a_horse_with_no_name's answer actually has a syntax error, missing a final closing right parens, but other than that is the correct way to do this.

更新: 对于像我这样的情况来到这里的人,如果你有需要进行类型转换的列(例如PG 9.5中的时间戳或uuids或jsonb),你必须在传递给查询的值中声明:

-- insert multiple if not exists
-- where another_column_name is of type uuid, with strings cast as uuids
-- where created_at and updated_at is of type timestamp, with strings cast as timestamps
WITH data (id, some_column_name, another_column_name, created_at, updated_at) AS (
  VALUES
    (<id value>, <some_column_name_value>, 'a5fa7660-8273-4ffd-b832-d94f081a4661'::uuid, '2016-06-13T12:15:27.552-07:00'::timestamp, '2016-06-13T12:15:27.879-07:00'::timestamp),
    (<id value>, <some_column_name_value>, 'b9b17117-1e90-45c5-8f62-d03412d407dd'::uuid, '2016-06-13T12:08:17.683-07:00'::timestamp, '2016-06-13T12:08:17.801-07:00'::timestamp)
)
INSERT INTO table_name (id, some_column_name, another_column_name, created_at, updated_at)
SELECT d.id, d.survey_id, d.arrival_uuid, d.gf_created_at, d.gf_updated_at
FROM data d
WHERE NOT EXISTS (SELECT 1 FROM table_name t WHERE t.id = d.id);

a_horse_with_no_name's回答今天救了我一个项目,但不得不进行这些调整以使其完美。