sysdate () заставляет Postgres игнорировать индекс и выполнять дорогостоящее последовательное сканирование - PullRequest
1 голос
/ 15 января 2020

Кто-нибудь когда-нибудь сталкивался с этим? Postgres Enterprise DB Advanced Server 11.5.12

sysdate() (Oracle проприетарный) приводит к сканированию Seq, в данном случае 4782 строк:

EXPLAIN SELECT p.id, p.practice
 FROM PatientStatistics ps
 INNER JOIN Patients p
   ON p.id=ps.patient
 WHERE ps.nextfutureapptdateservertime <= sysdate()
 ORDER BY p.id ASC;

Hash Join  (cost=799.81..1761.53 rows=4782 width=8)
   Hash Cond: (p.id = ps.patient)
   ->  Index Only Scan using patients_index3 on patients p  (cost=0.29..921.44 rows=15442 width=8)
   ->  Hash  (cost=644.11..644.11 rows=4782 width=4)
         ->  Seq Scan on patientstatistics ps  (cost=0.00..644.11 rows=4782 width=4)
               Filter: (nextfutureapptdateservertime <= sysdate)

Изменение на now() или current_timestamp (SQL Standard) устраняет проблему. Postgres правильно использует индекс:

EXPLAIN SELECT p.id, p.practice
FROM PatientStatistics ps
INNER JOIN Patients p
   ON p.id=ps.patient
WHERE ps.nextfutureapptdateservertime <= now()
ORDER BY p.id ASC;

Nested Loop  (cost=0.57..51.41 rows=17 width=8)
   ->  Index Only Scan using "patientstatisti_idx$$_0c9a0048" on patientstatistics ps  (cost=0.29..8.53 rows=17 width=4)
         Index Cond: (nextfutureapptdateservertime <= now())
   ->  Index Scan using patients_pk on patients p  (cost=0.29..2.52 rows=1 width=8)
         Index Cond: (id = ps.patient)

Интересно отметить различия в выводе этих функций:

SELECT now();
SELECT current_timestamp;

15-JAN-20 09:36:41.932741 -05:00
15-JAN-20 09:36:41.932930 -05:00

SELECT sysdate();

15-JAN-20 09:37:17

Возможно, индексы даты Postgres хешируются с использованием Datetime которые имеют десятичную часть. Планировщик видит, что ему была передана дата без десятичного числа, и он знает, что ключи индекса не будут точно выровнены, поэтому он возвращается к сканированию, чтобы убедиться, что запрос дает 100% точные результаты.

Я не смог ничего найти об этом онлайн после 30-минутного поиска в Google.

Ответы [ 3 ]

2 голосов
/ 15 января 2020

Я не знаю собственной вилки EDB, поэтому следующее основано на догадках:

now() или (эквивалентно) current_timestamp - это функция STABLE, поэтому она возвращает то же значение, если оно оценивается более одного раза в ходе выполнения оператора (и даже транзакции).

Подозрение заключается в том, что sysdate, как и PostgreSQL s clock_timestamp(), равно VOLATILE ( возвращает фактическое время).

Тогда функция может иметь различное значение при каждом сравнении со строкой, что делает невозможным использование сканирования индекса.

Если мое подозрение не правильно, я бы назвал это ошибкой EDB.

1 голос
/ 15 января 2020

Да. Должно быть, это волатильность. Документы PG по этому вопросу. https://www.postgresql.org/docs/8.2/xfunc-volatility.html

Они показывают "timeofday ()" в качестве примера Volatile.

  • now () - STABLE - время, когда этот запрос начался. Вызовите его 6 раз в одном и том же запросе, он возвращает одно и то же время.
  • timeofday () и sysdate () - VOLATILE - время в момент вызова функции time () ; не запрос. Это все равно что обстреливать инструмент date операционной системы. Назовите его 6 раз в одном запросе, вы получите 6 раз.
1 голос
/ 15 января 2020

Я не знаю как они реализовали это, но этот обходной путь работает правильно здесь:


CREATE OR REPLACE FUNCTION mysysdate(OUT timestamptz)
AS
$func$
select now();
$func$
language sql stable;

select mysysdate() ;

EXPLAIN select *
FROM public.feature_timeslice
WHERE valid_time_begin < mysysdate() - '10 year + 14 days'::interval;

select version() ;
\df+ mysysdate

Вывод:


CREATE FUNCTION
           mysysdate           
-------------------------------
 2020-01-15 17:15:13.896497+01
(1 row)

                                              QUERY PLAN                                               
-------------------------------------------------------------------------------------------------------
 Index Scan using feature_timeslice_alt2 on feature_timeslice  (cost=0.42..4474.84 rows=9206 width=28)
   Index Cond: (valid_time_begin < (now() - '10 years 14 days'::interval))
(2 rows)

                                                version                                                
-------------------------------------------------------------------------------------------------------
 PostgreSQL 11.3 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4, 64-bit
(1 row)

                                                                                       List of functions
 Schema |   Name    |     Result data type     |     Argument data types      | Type | Volatility | Parallel |  Owner   | Security | Access privileges | Language |  Source code  | Description 
--------+-----------+--------------------------+------------------------------+------+------------+----------+----------+----------+-------------------+----------+---------------+-------------
 tmp    | mysysdate | timestamp with time zone | OUT timestamp with time zone | func | stable     | unsafe   | postgres | invoker  |                   | sql      |              +| 
        |           |                          |                              |      |            |          |          |          |                   |          | select now();+| 
        |           |                          |                              |      |            |          |          |          |                   |          |               | 
(1 row)

Примечание: детализация не влияет на план запроса,

select date_trunc('sec', now());

также приводит к сканированию индекса.

...