Postgres Индексная функция появляется для создания различных значений в зависимости от того, когда она вызывается - PullRequest
2 голосов
/ 14 января 2020

Я наблюдаю за поведением, которое не могу объяснить в Postgres v9.6.

Таблица существует, назовем ее 'заказами' со схемой

create table orders(
  id uuid primary key,
  created_at timestamp with time zone not null,
  modified_at timestamp with time zone not null,
  entity jsonb not null);

Мне нужно создать индекс на основе функции, которая использует id и modified_at значения для создания значения индекса. Поэтому у меня есть

-- We put the 'A' on the front because if the beginning of the string looks like a date, it triggers
-- a postgres bug that causes sorting to skip some rows. The 'A' makes the value not look like a date.
create function sortable_timestamp_and_uuid(timestamp with time zone, uuid) returns text as
$$ select 'A' || to_char($1, 'YYYY-MM-DD HH24:MI:SS.US') || '/' || $2::text; $$
language sql immutable;

create function paging_func(timestamp with time zone, uuid) returns text as
$$ select sortable_timestamp_and_uuid($1, $2); $$
language sql immutable;

create index my_paging_idx on orders( paging_func(modified_at, id) );

Это работает, как и ожидалось. Я могу создать индекс для таблицы заказов, и когда я запускаю запрос с предложением WHERE, равным paging_func(modified_at, id) < pagine_func(some_specfic_timestamp, some_specific_uuid), он возвращает ожидаемые результаты.

HOWEVER это работает только для данных, которые уже существовали в таблице, когда я создал индекс. Если я вставлю данные в таблицу, INSERT id, created_at, modified_at, entity VALUES(?,now(),now(),?), мое предыдущее предложение where не будет работать для вновь вставленных данных. Данные отображаются в верхней части (наименьшего по значению) индекса.

Например, если у меня есть две строки со значениями:

id                                    | modified_at
--------------------------------------------------------------------
199967e2-0987-2987-11c7-bbca1348467e  | 2020-01-14 20:14:25.799287
298bc22a-6eaa-5ec3-d962-ad2d206a4dca  | 2020-01-14 20:14:25.799287

Если я создаю индекс уже со строками существует база данных и запрос с

WHERE paging_func(modified_at, id) < paging_func(to_timestamp('2020-01-14 20:14:25.799287',
          'YYYY/MM/DD HH24:MI:SS.US'),
          uuid_in('298bc22a-6eaa-5ec3-d962-ad2d206a4dca'))

Набор результатов будет содержать только первую строку. Однако, если при создании индекса существует только первая строка, я вставляю вторую строку в таблицу и выполняю тот же точный запрос , возвращаются обе строки.

Если я удаляю индекс и воссоздаю индекс, индекс ведет себя так, как ожидается для данных в таблице, но все новые значения, вставленные в таблицу, не проиндексированы правильно. Буду признателен за помощь в объяснении того, что я делаю неправильно и почему я наблюдаю за этим поведением.

Ответы [ 2 ]

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

Причина в том, что вы лгали, когда говорили, что функция неизменна:

SET timezone = 'UTC';

SELECT sortable_timestamp_and_uuid('2020-01-01 00:00:00+00',
                                   '9a1b6ef4-370f-11ea-9c8d-d0c637b5521b');

                   sortable_timestamp_and_uuid                    
------------------------------------------------------------------
 A2020-01-01 00:00:00.000000/9a1b6ef4-370f-11ea-9c8d-d0c637b5521b
(1 row)

SET timezone = 'Europe/Vienna';

SELECT sortable_timestamp_and_uuid('2020-01-01 00:00:00+00',
                                   '9a1b6ef4-370f-11ea-9c8d-d0c637b5521b');

                   sortable_timestamp_and_uuid                    
------------------------------------------------------------------
 A2020-01-01 01:00:00.000000/9a1b6ef4-370f-11ea-9c8d-d0c637b5521b
(1 row)

Так что, когда timezone отличается, когда вы пишете строку и пытаетесь SELECT ее запрос может не найти строку. Короче говоря, повреждение данных.

Используйте такую ​​функцию:

CREATE FUNCTION sortable_timestamp_and_uuid(timestamp with time zone, uuid) RETURNS text AS
$$SELECT 'A' || ($1 AT TIME ZONE 'UTC')::text || '/' || $2::text$$
LANGUAGE sql IMMUTABLE;
0 голосов
/ 15 января 2020

Вся функция не нужна.

Создайте индекс из нескольких столбцов:

create index my_paging_idx on orders(modified_at, id);

Затем измените предложение WHERE на:

WHERE (modified_at, id) < (timestamp '2020-01-14 20:14:25.799287', '298bc22a-6eaa-5ec3-d962-ad2d206a4dca'::uuid)

Должно быть столь же эффективно, и индекс будет меньше а также он содержит двоичное представление обоих значений, а не строк.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...