违反唯一约束的行导致整个 pq.CopyIn postgresql 导入失败
unique constraint violation on a row causes entire pq.CopyIn postgresql import to fail
我正在尝试使用 pq.CopyIn 进行批量导入,如下所述:
https://godoc.org/github.com/lib/pq
导入比我尝试过的其他方法快得多,但我发现仅在一条记录中违反唯一约束将导致整个导入失败。
有什么方法可以使用 pq.CopyIn.
设置 ON CONFLICT DO NOTHING
这是我的 table 结构的副本
CREATE TABLE test (
id serial PRIMARY KEY,
unique_token VARCHAR ( 10 ) UNIQUE NOT NULL,
frequency INT DEFAULT 0
);
我尝试使用下面的@mkopriva 回答,但出现错误:pq: null
“id”列中的值违反了非空约束
下面的代码示例
tx, _ := db.Begin()
_, err = tx.Exec(`CREATE TEMP TABLE token_temp ON COMMIT DROP AS
SELECT id, unique_token FROM test WITH NO DATA`)
if err != nil {
return err
}
stmt, err := tx.Prepare(pq.CopyIn("token_temp", "unique_token"))
if err != nil {
fmt.Println("error here")
return err
}
for _, token := range tokenList {
_, err = stmt.Exec(token)
if err != nil {
return err
}
}
_, err = stmt.Exec()
if err != nil {
log.Fatal(err)
}
err = stmt.Close()
if err != nil {
log.Fatal(err)
}
_, err = tx.Exec(`INSERT INTO test SELECT id, unique_token FROM
token_temp ON CONFLICT(unique_token) DO UPDATE SET frequency=
test.frequency + 1 `)
if err != nil {
fmt.Println("Error")
return err
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
pq.CopyIn
内部使用不支持 ON CONFLICT
子句的 COPY FROM
。
但是,您可以做的是创建一个没有约束的临时 table,将数据复制到该临时 table,然后执行 INSERT
目标 table,使用 ON CONFLICT
子句,使用临时 table 作为要插入的数据源。
一个例子应该更清楚,假设你有一个 users
table 看起来像这样:
CREATE TABLE users (
id serial PRIMARY KEY
, name text
, email text UNIQUE
);
假设您有一部分这样的用户:
var users = []User{
{Name: "John Doe", Email: "jdoe@example.com"},
{Name: "Joe Blow", Email: "jblow@example.com"},
{Name: "Jane Doe", Email: "jdoe@example.com"}, // duplicate email!
{Name: "Foo Bar", Email: "fbar@example.com"},
}
有了它,您可以执行以下操作:
_, err = txn.Exec(`
CREATE TEMP TABLE users_temp
ON COMMIT DROP
AS SELECT * FROM users
WITH NO DATA`)
if err != nil {
panic(err)
}
stmt, err := txn.Prepare(pq.CopyIn("users_temp", "name", "email"))
if err != nil {
panic(err)
}
for _, u := range users {
if _, err := stmt.Exec(u.Name, u.Email); err != nil {
panic(err)
}
}
if _, err := stmt.Exec(); err != nil {
panic(err)
}
if err := stmt.Close(); err != nil {
panic(err)
}
_, err = txn.Exec(`
INSERT INTO users (name, email)
SELECT name, email FROM users_temp
ON CONFLICT DO NOTHING`)
if err != nil {
panic(err)
}
if err := txn.Commit(); err != nil {
panic(err)
}
在你 运行 上面你可以做 SELECT * FROM users;
你会得到这个:
id | name | email
----+----------+-------------------
1 | John Doe | jdoe@example.com
2 | Joe Blow | jblow@example.com
4 | Foo Bar | fbar@example.com
(3 rows)
对于您的具体示例和要求,您可以在 INSERT ... SELECT ...
查询中执行类似的操作:
_, err = txn.Exec(`
INSERT INTO test (unique_token, frequency)
SELECT unique_token, COUNT(*) FROM token_temp
GROUP BY unique_token`)
if err != nil {
panic(err)
}
我正在尝试使用 pq.CopyIn 进行批量导入,如下所述:
https://godoc.org/github.com/lib/pq
导入比我尝试过的其他方法快得多,但我发现仅在一条记录中违反唯一约束将导致整个导入失败。
有什么方法可以使用 pq.CopyIn.
设置 ON CONFLICT DO NOTHING这是我的 table 结构的副本
CREATE TABLE test (
id serial PRIMARY KEY,
unique_token VARCHAR ( 10 ) UNIQUE NOT NULL,
frequency INT DEFAULT 0
);
我尝试使用下面的@mkopriva 回答,但出现错误:pq: null “id”列中的值违反了非空约束
下面的代码示例
tx, _ := db.Begin()
_, err = tx.Exec(`CREATE TEMP TABLE token_temp ON COMMIT DROP AS
SELECT id, unique_token FROM test WITH NO DATA`)
if err != nil {
return err
}
stmt, err := tx.Prepare(pq.CopyIn("token_temp", "unique_token"))
if err != nil {
fmt.Println("error here")
return err
}
for _, token := range tokenList {
_, err = stmt.Exec(token)
if err != nil {
return err
}
}
_, err = stmt.Exec()
if err != nil {
log.Fatal(err)
}
err = stmt.Close()
if err != nil {
log.Fatal(err)
}
_, err = tx.Exec(`INSERT INTO test SELECT id, unique_token FROM
token_temp ON CONFLICT(unique_token) DO UPDATE SET frequency=
test.frequency + 1 `)
if err != nil {
fmt.Println("Error")
return err
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
pq.CopyIn
内部使用不支持 ON CONFLICT
子句的 COPY FROM
。
但是,您可以做的是创建一个没有约束的临时 table,将数据复制到该临时 table,然后执行 INSERT
目标 table,使用 ON CONFLICT
子句,使用临时 table 作为要插入的数据源。
一个例子应该更清楚,假设你有一个 users
table 看起来像这样:
CREATE TABLE users (
id serial PRIMARY KEY
, name text
, email text UNIQUE
);
假设您有一部分这样的用户:
var users = []User{
{Name: "John Doe", Email: "jdoe@example.com"},
{Name: "Joe Blow", Email: "jblow@example.com"},
{Name: "Jane Doe", Email: "jdoe@example.com"}, // duplicate email!
{Name: "Foo Bar", Email: "fbar@example.com"},
}
有了它,您可以执行以下操作:
_, err = txn.Exec(`
CREATE TEMP TABLE users_temp
ON COMMIT DROP
AS SELECT * FROM users
WITH NO DATA`)
if err != nil {
panic(err)
}
stmt, err := txn.Prepare(pq.CopyIn("users_temp", "name", "email"))
if err != nil {
panic(err)
}
for _, u := range users {
if _, err := stmt.Exec(u.Name, u.Email); err != nil {
panic(err)
}
}
if _, err := stmt.Exec(); err != nil {
panic(err)
}
if err := stmt.Close(); err != nil {
panic(err)
}
_, err = txn.Exec(`
INSERT INTO users (name, email)
SELECT name, email FROM users_temp
ON CONFLICT DO NOTHING`)
if err != nil {
panic(err)
}
if err := txn.Commit(); err != nil {
panic(err)
}
在你 运行 上面你可以做 SELECT * FROM users;
你会得到这个:
id | name | email
----+----------+-------------------
1 | John Doe | jdoe@example.com
2 | Joe Blow | jblow@example.com
4 | Foo Bar | fbar@example.com
(3 rows)
对于您的具体示例和要求,您可以在 INSERT ... SELECT ...
查询中执行类似的操作:
_, err = txn.Exec(`
INSERT INTO test (unique_token, frequency)
SELECT unique_token, COUNT(*) FROM token_temp
GROUP BY unique_token`)
if err != nil {
panic(err)
}