sql - Postgres 使用 `split_part` 索引

标签 sql postgresql indexing split jsonb

上下文:

我有一个测试表:

=> \d+ test 
                                       Table "public.test"
Column     |          Type          | Collation | Nullable | Default | Storage  | Stats target | Description 
---------------+------------------------+-----------+----------+---------+----------+-------- 
------+-------------
 id            | character varying(255) |           |          |         | extended |              
| 
 configuration | jsonb                  |           |          |         | extended |              
| 

configuration 列包含“定义良好”的 json,它有一个名为 source_url 的键(跳过其他不相关的键)。 configuration 列的示例值是:

{
"source_url": "https://<resource-address>?Signature=R1UzTGphWEhrTTFFZnc0Q4qkGRxkA5%2BHFZSfx3vNEvRsrlDcHdntArfHwkWiT7Qxi%2BWVJ4DbHJeFp3GpbS%2Bcb1H3r1PXPkfKB7Fjr6tFRCetDWAOtwrDrVOkR9G1m7iOePdi1RW%2Fn1LKE7MzQUImpkcZXkpHTUgzXpE3TPgoeVtVOXXt3qQBARpdSixzDU8dW%2FcftEkMDVuj4B%2Bwiecf6st21MjBPjzD4GNVA%2F6bgvKA6ExrdYmM5S6TYm1lz2e6juk81%2Fk4eDecUtjfOj9ekZiGJVMyrD5Tyw%2FTWOrfUB2VM1uw1PFT2Gqet87jNRDAtiIrJiw1lfB7Od1AwNxIk0Rqkrju8jWxmQhvb1BJLV%2BoRH56OHdm5nHXFmQdldVpyagQ8bQXoKmYmZPuxQb6t9FAyovGMav3aMsxWqIuKTxLzjB89XmgwBTxZSv5E9bkWUbom2%2BWq4O3%2BCrVxYwsqg%3D%3D&Expires-At=1569340020&Issued-At=1568293200"
    .
    .
}

URL 包含一个查询参数 Expires-At


问题:

有一个每 24 小时运行一次的计划作业。这项工作应该找到所有这些已过期/即将过期的记录(然后对其进行处理)。

解决方案:

我有这个查询来完成我的工作:

select * from test where to_timestamp(split_part(split_part(configuration->>'source_url', 'Expires-At=', 2), '&', 1)::bigint) <= now() + interval '24 hours';

解释:

  • 查询首先在 Expires-At= 处拆分 source_url 并选择它右侧的部分,然后在 & 处拆分结果字符串 并选择它的左侧部分,从而获得 text
  • 所需的确切纪元时间
  • Expires-Atsource_url 中的最后一个查询参数时,相同的查询也适用于极端情况
  • 一旦将纪元时间提取为 text,它首先将其转换为 bigint,然后将其转换为 Postgres 时间戳,然后比较此时间戳是否要进行小于或等于距 now()
  • 24 小时的时间
  • 所有满足上述条件的行都被选中

因此,在每次运行的最后,调度程序会刷新所有将在接下来的 24 小时内过期的 url(包括那些已经过期的 url)


问题:

  1. 虽然这解决了我的问题,但我真的不喜欢这个解决方案。这有很多我觉得不干净的字符串操作。有没有更简洁的方法来做到这一点?
  2. 如果我们“必须”采用上述解决方案,我们甚至可以为此类查询使用索引吗?我知道函数 lower(), upper() extra 可以被索引,但我真的想不出任何方法可以索引这个查询。

备选方案:

除非有一个真正干净的解决方案,否则我将采用以下方法:

  • 我会在 configuration json 中引入一个名为 expires_at 的新键,确保每次插入一行时它都会填充正确的值。
  • 然后直接查询这个新添加的字段(索引在configuration列)。

我承认我通过这种方式重复了信息 Expires-At,但在我能想到的所有可能解决方案中,这是我发现最干净的解决方案。

你们能想到比这更好的方法吗?



编辑:

更新查询以使用 substring() 和正则表达式而不是内部 split_part():

select * from test where to_timestamp(split_part(substring(configuration->>'source_url' from 'Expires-At=\d+'), '=', 2)::bigint) <= now() + interval '24 hours';

最佳答案

鉴于您当前的数据模型,我认为您的 WHERE 条件没有那么糟糕。

你可以用它来索引

CREATE INDEX ON test ( 
   to_timestamp(
      split_part(
         split_part(
            configuration->>'source_url',
            'Expires-At=',
            2
         ),
         '&',
         1
      )::bigint
   )
);

本质上,您必须为 = 左侧的整个表达式建立索引。只有当涉及的所有函数和运算符都是 IMMUTABLE 时,您才能这样做,我认为它们就是您的情况。

不过我会更改数据模型。首先,我看不到包含单个值的 jsonb 列的值(value)。为什么不用 URL 作为 text 列呢?

您可以走得更远,将 URL 拆分为单独的部分,这些部分存储在列中。

这一切是否是一个好主意取决于您如何使用数据库中的值:通常将您在 WHERE 条件和喜欢并让其余的“一团糟”。这在某种程度上是一个品味问题。

关于sql - Postgres 使用 `split_part` 索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58082958/

相关文章:

sql - 如何使用 ActiveRecord 和 Postgresql 按列选择唯一记录

sql - 有什么方法可以安全地运行 SELECT INTO 吗?

java - 日期在Java中的非英语操作系统中即将到来

sql - Hive 和 SparkSQL 不支持日期时间类型?

postgresql - Postgres 比较来自 3 个不同表的相同行、计数和匹配

python - 通过 groupby 函数选择组

mongodb - MongoDB 使用什么样的公式来计算地球圈索引中的距离?

python - 我需要在这个 Django 模型中添加一个 db_index 吗?

sql - 计算列规范中的除法、求和值

mysql - IF语句MYSQL中的SUBSTR()函数