我使用Python写postgres数据库:

sql_string = "INSERT INTO hundred (name,name_slug,status) VALUES ("
sql_string += hundred + ", '" + hundred_slug + "', " + status + ");"
cursor.execute(sql_string)

但由于我的一些行是相同的,我得到以下错误:

psycopg2.IntegrityError: duplicate key value  
  violates unique constraint "hundred_pkey"

我怎么能写一个'插入,除非这行已经存在' SQL语句?

我见过这样的复杂语句:

IF EXISTS (SELECT * FROM invoices WHERE invoiceid = '12345')
UPDATE invoices SET billed = 'TRUE' WHERE invoiceid = '12345'
ELSE
INSERT INTO invoices (invoiceid, billed) VALUES ('12345', 'TRUE')
END IF

但首先,这对我需要的东西来说是不是太过了,其次,我怎么能把它们作为一个简单的字符串来执行呢?


当前回答

有规则很简单:

CREATE RULE file_insert_defer AS ON INSERT TO file
WHERE (EXISTS ( SELECT * FROM file WHERE file.id = new.id)) DO INSTEAD NOTHING

但是并发写失败了…

其他回答

你可以在Postgres中使用VALUES:

INSERT INTO person (name)
    SELECT name FROM person
    UNION 
    VALUES ('Bob')
    EXCEPT
    SELECT name FROM person;

我怎么能写一个'插入,除非这行已经存在' SQL语句?

在PostgreSQL中有一个很好的方法来执行有条件的INSERT:

INSERT INTO example_table
    (id, name)
SELECT 1, 'John'
WHERE
    NOT EXISTS (
        SELECT id FROM example_table WHERE id = 1
    );

注意:对于并发写操作,这种方法不是100%可靠的。在NOT EXISTS反半连接中的SELECT和INSERT本身之间有一个非常小的竞争条件。在这种情况下,它可能会失败。

INSERT INTO invoices (invoiceid, billed) (
    SELECT '12345','TRUE' WHERE NOT EXISTS (
        SELECT 1 FROM invoices WHERE invoiceid='12345' AND billed='TRUE'
        )
)

一种方法是创建一个不受约束(没有唯一索引)的表,将所有数据插入其中,并执行与插入到百位表不同的选择。

所以高水平。我假设在我的示例中所有三个列都是不同的,因此对于step3,将NOT EXITS连接更改为只连接100表中唯一的列。

Create temporary table. See docs here. CREATE TEMPORARY TABLE temp_data(name, name_slug, status); INSERT Data into temp table. INSERT INTO temp_data(name, name_slug, status); Add any indexes to the temp table. Do main table insert. INSERT INTO hundred(name, name_slug, status) SELECT DISTINCT name, name_slug, status FROM hundred WHERE NOT EXISTS ( SELECT 'X' FROM temp_data WHERE temp_data.name = hundred.name AND temp_data.name_slug = hundred.name_slug AND temp_data.status = status );

我们可以使用upsert简化查询

insert into invoices (invoiceid, billed) 
  values ('12345', 'TRUE') 
  on conflict (invoiceid) do 
    update set billed=EXCLUDED.billed;