У нас есть сценарий использования в нашем приложении, где мы сначала копируем CSV-файл в таблицу staging , а затем вставляем проверенные данные во вторую таблицу участников .Вновь созданный идентификатор участника (первичный ключ) затем обновляется в таблице staging для дальнейшей обработки.
В нашем приложении мы постоянно сталкиваемся с проблемами производительности.Иногда эта процедура работает с 100 000 строк за 15-20 секунд.Иногда это никогда не закончится в разумное время (pg_cancel_backend к спасению).
Когда я попытался создать настолько достойный минимальный тестовый пример, я не смог воспроизвести проблему: /.Так что это попытка получить некоторый совет, как отлаживать дальше или переписать базовый запрос.
- PHP-приложение с Doctrine DBAL
- Postgres 10,5
Мы делаем это с помощью CTE - в основном так:
WITH inserted_participants AS (
INSERT INTO participants (email, project_id, survey_token, participant_uname)
SELECT
staging.email,
1,
staging.generated_token,
staging.email -- is used as uname
FROM
staging
RETURNING
participants.participant_id,
participants.participant_uname
) -- Update existing staging data with newly created participant_id
UPDATE
staging AS stage_update
SET
resulting_participant_id = inserted_participants.participant_id
FROM
inserted_participants
WHERE stage_update.email = inserted_participants.participant_uname;
Еще раз: я не могу воспроизвести проблемы с производительностью в этом тестовом примере.Я подозреваю, что это как-то связано с CTE.
Можно ли это переписать без использования CTE и при этом безопасно возвращать вновь созданные строки и обновлять их в промежуточной таблице?
Это структура таблицы для минимального тестового случая:
CREATE EXTENSION IF NOT EXISTS citext;
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
DROP TABLE IF EXISTS public.staging;
CREATE TABLE public.staging
(
staging_id serial,
email citext COLLATE pg_catalog."default",
generated_token character varying(255) COLLATE pg_catalog."default",
resulting_participant_id integer,
CONSTRAINT staging_pkey PRIMARY KEY (staging_id),
CONSTRAINT unique_generated_token UNIQUE (generated_token)
);
CREATE INDEX ON public.staging (email);
CREATE INDEX ON public.staging (generated_token);
DROP TABLE IF EXISTS public.participants;
CREATE TABLE public.participants
(
participant_id serial,
email citext COLLATE pg_catalog."default" NOT NULL,
project_id integer NOT NULL,
survey_token character varying(255) COLLATE pg_catalog."default" NOT NULL,
participant_uname citext COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT participants_pkey PRIMARY KEY (participant_id),
CONSTRAINT participants_participant_uname_project_id_key UNIQUE (participant_uname, project_id),
CONSTRAINT participants_project_id_email_key UNIQUE (project_id, email),
CONSTRAINT participants_project_id_participant_uname_key UNIQUE (project_id, participant_uname),
CONSTRAINT participants_survey_token_key UNIQUE (survey_token)
);
CREATE INDEX ON public.participants (participant_uname);
CREATE INDEX ON public.participants (project_id);
И фиктивные данные, которые я использовал:
INSERT INTO
staging (staging_id, email, generated_token)
SELECT
generate_series(1,100000),
gen_random_uuid()::citext,
gen_random_uuid()::TEXT;