Это сервер, на котором я работаю
select version();
version
---------------------------------------------------------------------------
PostgreSQL 10.6 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
(1 row)
Я начал с написания select (ext.t_event и ext.t_event_data - две сторонние таблицы, которые oracle_fdw (версия 1.1) берет из удаленной базы данных oracle)
select
te.id_data,
te.id_device,
te.date_write,
te.date_event,
ted.i_inout,
ted.value
from ext.t_event te, ext.t_event_data ted
where te.id_device =2749651
and te.date_event >= '2019-01-16'and te.date_event < '2019-01-17'
and te.id_data=ted.id_data;
Требуется около 10 секунд, чтобы получить весь набор записей (3600 записей).
Но потом я превратил выбор в выбор вставки
insert into stg_data
select
te.id_data,
te.id_device,
te.date_write,
te.date_event,
ted.i_inout,
ted.value
from ext.t_event te, ext.t_event_data ted
where te.id_device =2749651
and te.date_event >= '2019-01-16'and te.date_event < '2019-01-17'
and te.id_data=ted.id_data;
и я был вынужден убить запрос, он работал более 30 минут!
После нескольких часов борьбы и отчаянных попыток я решил попробовать этот
insert into stg_data
select
te.id_data,
te.id_device,
te.date_write,
te.date_event,
ted.i_inout,
ted.value
from ext.t_event te, ext.t_event_data ted
where te.id_device =2749651
and te.date_event >= '2019-01-16'and te.date_event < '2019-01-17'
and te.id_data=ted.id_data
limit 5000;
и ... неожиданный сюрприз за 20 секунд. У меня весь набор записей хранится в stg_data.
Чтобы лучше понять различия, я решил проанализировать планы.
ВЫБРАТЬ БЕЗ ОГРАНИЧЕНИЯ
Foreign Scan (cost=10000.00..20000.00 rows=1000 width=548)
Oracle query: SELECT /*eb01c463a72c3b6350f86f5db25e1353*/ r1."ID_DATA",
r1."ID_DEVICE", r1."DATE_WRITE", r1."DATE_EVENT", r2."I_INOUT",
r2."VALUE" FROM ("DISPATCH"."T_EVENT" r1 INNER JOIN
"DISPATCH"."T_EVENT_DATA" r2 ON (r1."ID_DATA" = r2."ID_DATA") AND
(r1."DATE_EVENT" >= (CAST ('2019-01-16 00:00:00.000000 AD' AS
TIMESTAMP))) AND (r1."DATE_EVENT" <
(CAST ('2019-01-17 00:00:00.000000 AD' AS TIMESTAMP)))
AND (r1."ID_DEVICE" = 2749651))
ВЫБРАТЬ С ОГРАНИЧЕНИЕМ
Limit (cost=10000.00..20000.00 rows=1000 width=548)
-> Foreign Scan (cost=10000.00..20000.00 rows=1000 width=548)
Oracle query: SELECT /*eb01c463a72c3b6350f86f5db25e1353*/
r1."ID_DATA", r1."ID_DEVICE", r1."DATE_WRITE", r1."DATE_EVENT",
r2."I_INOUT", r2."VALUE" FROM ("DISPATCH"."T_EVENT" r1 INNER
JOIN "DISPATCH"."T_EVENT_DATA" r2 ON (r1."ID_DATA" = r2."ID_DATA")
AND (r1."DATE_EVENT" >= (CAST ('2019-01-16 00:00:00.000000 AD' AS
TIMESTAMP))) AND (r1."DATE_EVENT" < (CAST ('2019-01-17
00:00:00.000000 AD' AS TIMESTAMP))) AND (r1."ID_DEVICE" = 2749651))
Таким образом, он в основном отправляет тот же запрос в Oracle и применяет ФИЛЬТР локально, как только выборка завершена.
План INSER-SELECT выглядит одинаково? NOPE!
INSERT_SELECT с LIMIT
Insert on stg_data_hist (cost=10000.00..20010.00 rows=1000 width=548)
-> Limit (cost=10000.00..20000.00 rows=1000 width=548)
-> Foreign Scan (cost=10000.00..20000.00 rows=1000 width=548)
Oracle query: SELECT /*eb01c463a72c3b6350f86f5db25e1353*/
r1."ID_DATA", r1."ID_DEVICE", r1."DATE_WRITE",
r1."DATE_EVENT", r2."I_INOUT", r2."VALUE" FROM
("DISPATCH"."T_EVENT" r1 INNER JOIN
"DISPATCH"."T_EVENT_DATA" r2 ON (r1."ID_DATA" =
r2."ID_DATA") AND (r1."DATE_EVENT" >= (CAST ('2019-01-16
00:00:00.000000 AD' AS TIMESTAMP))) AND (r1."DATE_EVENT" <
(CAST('2019-01-17 00:00:00.000000 AD' AS TIMESTAMP))) AND
(r1."ID_DEVICE" = 2749651))
INSERT-SELECT no LIMIT предложение
Insert on stg_data_hist (cost=30012.50..40190.00 rows=5000 width=548)
-> Hash Join (cost=30012.50..40190.00 rows=5000 width=548)
Hash Cond: (te.id_data = ted.id_data)
-> Foreign Scan on t_event te (cost=10000.00..20000.00 rows=1000 width=28)
Oracle query: SELECT /*93379c271b3f1bc08a1dbb94fb89f739*/
r3."ID_DATA", r3."ID_DEVICE", r3."DATE_WRITE", r3."DATE_EVENT"
FROM "DISPATCH"."T_EVENT" r3 WHERE (r3."DATE_EVENT" >=
(CAST ('2019-01-16 00:00:00.000000 AD' AS TIMESTAMP))) AND
(r3."DATE_EVENT" < (CAST ('2019-01-17 00:00:00.000000 AD' AS
TIMESTAMP))) AND (r3."ID_DEVICE" = 2749651)
-> Hash (cost=20000.00..20000.00 rows=1000 width=528)
-> Foreign Scan on t_event_data ted
(cost=10000.00..20000.00 rows=1000 width=528)
Oracle query: SELECT /*21c8741f2fa8a8d13d037c3191e8ac96*/
r4."ID_DATA", r4."I_INOUT", r4."VALUE" FROM
"DISPATCH"."T_EVENT_DATA" r4
И это объясняет, почему это занимает ваааааай дольше, чем другие. Он извлекает отфильтрованные по дате записи из одной внешней таблицы, полный набор из второй внешней таблицы и выполняет локальное объединение. Это займет много времени !! Это несколько миллионов записей против нескольких тысяч.
И наконец два моих вопроса
1) Я хочу иметь первый план, но избавлюсь от предложения LIMIT (пошли дрожи по спине :-)). Как бы Вы это сделали? Я не имею в виду применять фильтры к ext.t_event_data, кроме как из условия соединения.
2) Почему два плана INSERT-SELECT выглядят такими разными, даже если два плана SELECT выглядят одинаково?
Спасибо за чтение и хорошего дня