У меня была та же проблема, и я искал здесь много дней, чтобы собрать множество подсказок, чтобы сформировать законченное решение. Даже если вопрос устарел, надеюсь, это будет полезно другим.
1) Забудьте об удалении индексов / ограничений и воссоздании их позже, выгоды незначительны или хуже.
2) executemany лучше, чем execute, поскольку он делает для вас оператором prepare. Вы можете получить те же результаты самостоятельно с помощью команды, подобной следующей, чтобы набрать 300% скорости:
# To run only once:
sqlCmd = """PREPARE myInsert (int, timestamp, real, text) AS
INSERT INTO myBigTable (idNumber, date_obs, result, user)
SELECT $1, $2, $3, $4 WHERE NOT EXISTS
(SELECT 1 FROM myBigTable WHERE (idNumber, date_obs, user)=($1, $2, $4));"""
curPG.execute(sqlCmd)
cptInsert = 0 # To let you commit from time to time
#... inside the big loop:
curPG.execute("EXECUTE myInsert(%s,%s,%s,%s);", myNewRecord)
allreadyExists = (curPG.rowcount < 1)
if not allreadyExists:
cptInsert += 1
if cptInsert % 10000 == 0:
conPG.commit()
Этот пример фиктивной таблицы имеет уникальное ограничение (idNumber, date_obs, user).
3) Лучшее решение - использовать COPY_FROM и TRIGGER для управления уникальным ключом ПЕРЕД ВСТАВКОЙ. Это дало мне в 36 раз больше скорости. Я начал с обычных вставок со скоростью 500 записей в секунду. и с «копией» я получил более 18 000 записей в секунду. Пример кода на Python с Psycopg2:
ioResult = StringIO.StringIO() #To use a virtual file as a buffer
cptInsert = 0 # To let you commit from time to time - Memory has limitations
#... inside the big loop:
print >> ioResult, "\t".join(map(str, myNewRecord))
cptInsert += 1
if cptInsert % 10000 == 0:
ioResult = flushCopyBuffer(ioResult, curPG)
#... after the loop:
ioResult = flushCopyBuffer(ioResult, curPG)
def flushCopyBuffer(bufferFile, cursorObj):
bufferFile.seek(0) # Little detail where lures the deamon...
cursorObj.copy_from(bufferFile, 'myBigTable',
columns=('idNumber', 'date_obs', 'value', 'user'))
cursorObj.connection.commit()
bufferFile.close()
bufferFile = StringIO.StringIO()
return bufferFile
Вот и все, что касается Python. Теперь триггер Postgresql не должен иметь исключение psycopg2.IntegrityError, а затем все записи команды COPY отклоняются:
CREATE OR REPLACE FUNCTION chk_exists()
RETURNS trigger AS $BODY$
DECLARE
curRec RECORD;
BEGIN
-- Check if record's key already exists or is empty (file's last line is)
IF NEW.idNumber IS NULL THEN
RETURN NULL;
END IF;
SELECT INTO curRec * FROM myBigTable
WHERE (idNumber, date_obs, user) = (NEW.idNumber, NEW.date_obs, NEW.user);
IF NOT FOUND THEN -- OK keep it
RETURN NEW;
ELSE
RETURN NULL; -- Oups throw it or update the current record
END IF;
END;
$BODY$ LANGUAGE plpgsql;
Теперь свяжите эту функцию с триггером вашей таблицы:
CREATE TRIGGER chk_exists_before_insert
BEFORE INSERT ON myBigTable FOR EACH ROW EXECUTE PROCEDURE chk_exists();
Это кажется большой работой, но Postgresql - очень быстрый зверь, когда ему не нужно интерпретировать SQL снова и снова. Веселитесь.