sql - Postgres 索引函数似乎根据调用时间创建不同的值
问题描述
我正在观察我无法在 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) );
这按预期工作。我可以在 orders 表上创建一个索引,当我使用它的WHERE
子句运行查询时,paging_func(modified_at, id) < pagine_func(some_specfic_timestamp, some_specific_uuid)
会返回我期望的结果。
但是,这只适用于我创建索引时表中已经存在的数据。如果我将数据插入表中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'))
结果集将仅包含第一行。但是,如果在创建索引时仅存在第一行,并且我将第二行插入表中,并运行相同的精确查询,则会返回两行。
如果我删除索引并重新创建索引,则索引的行为与表中数据的预期相同,但插入到表中的所有新值都没有正确索引。我将不胜感激任何帮助解释我做错了什么以及为什么我观察到这种行为。
解决方案
原因是当你说函数是不可变的时你在撒谎:
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;
推荐阅读
- node.js - 在 electronjs 应用程序中崩溃时重新启动 nodejs 服务器
- java - ElasticSearch 使用 java API“ScrolledPage.class”,我想要 getId
- c++ - 使用了未初始化的局部变量“快速”
- java - 无法理解此 java 代码的输出
- rx-java - 即使与 defer 一起使用,反应式 zipWith 也不懒惰
- mysql - 当条件对第二个表没有结果时,如何在 LEFT JOIN 上返回 NULL
- go - 开始时遇到困难。`package main`抛出运行时错误-索引超出范围?
- php - 如何创建类似的 php 页面但具有不同的 url
- amazon-ecs - 使用 Fargate 在 AWS 上部署哨兵
- sql - Sql Pivot 准时