假设我的用户表定义如下:
CREATE TABLE user (
id SERIAL UNIQUE,
email character varying(254) NOT NULL UNIQUE,
username character varying(254) NOT NULL UNIQUE
)
并以这种方式触发它:
CREATE OR REPLACE FUNCTION insert_username() RETURNS
TRIGGER AS $insert_username$
BEGIN
NEW.username := SPLIT_PART(NEW.email, '@', 1);
RETURN NEW;
END;
$insert_username$ LANGUAGE plpgsql;
CREATE TRIGGER insert_username BEFORE INSERT OR UPDATE
ON user FOR EACH ROW EXECUTE
PROCEDURE insert_username();
然后使用psycopg2尝试用来自另一个数据库的数据填充该表。这是我制作的迁移模块中的代码片段以及负责此操作的查询字符串:
from psycopg2.extras import execute_values
from data_migration.database import cursor
query = 'INSERT INTO user("id", "email") VALUES %s'
values = [[1, 'example1@email.com'], [2, 'example2@email.com']]
execute_values(cursor, query, values)
在我实施此触发器之前,它一直按预期工作。现在,当我从postgres shell插入单行或使用psycopg2时,它仍然可以正常工作,但是即使在表完全为空的情况下,execute_values也失败,并给我UniqueViolation错误:
UniqueViolation: duplicate key value violates unique constraint "user_username_key"
DETAIL: Key (username)=(kjhmgfd) already exists.
围绕它有更多的字段和代码,当然,我可以摆脱此触发器,并在python端执行相同的操作。但是我真的很想保留它。也许我遗漏了一些明显的东西,但是我花了几个小时而仍然不知道为什么会发生。如果有人可以帮助我找到答案,那将非常有帮助。谢谢!
答案 0 :(得分:0)
似乎这是我对源数据的关注不足。那里有一些用户具有相同的本地部分,但电子邮件中的域不同。感谢Jeremy指出。最终以这种方式修改了触发器:
CREATE OR REPLACE FUNCTION insert_username() RETURNS
TRIGGER AS $insert_username$
DECLARE
counter INTEGER := 0;
new_username VARCHAR(256);
attempt_username VARCHAR(256);
is_exists BOOLEAN;
BEGIN
CREATE OR REPLACE FUNCTION is_username_exists(un VARCHAR(256))
RETURNS BOOLEAN AS $$
DECLARE
is_row_exists BOOLEAN;
BEGIN
SELECT EXISTS(SELECT * FROM user WHERE username=un) INTO is_row_exists;
RETURN is_row_exists;
END;
$$ LANGUAGE plpgsql;
new_username := SPLIT_PART(NEW.email, '@', 1);
attempt_username := new_username;
LOOP
SELECT is_username_exists(attempt_username) INTO is_exists;
EXIT WHEN is_exists IS FALSE;
counter := counter + 1;
attempt_username := new_username || counter;
END LOOP;
NEW.username := attempt_username;
RETURN NEW;
END;
$insert_username$ LANGUAGE plpgsql;
CREATE TRIGGER insert_username BEFORE INSERT OR UPDATE
ON user FOR EACH ROW EXECUTE PROCEDURE insert_username();