gpt4 book ai didi

sql - 在 Postgres jsonb 中查询数组结构的正确索引是什么?

转载 作者:行者123 更新时间:2023-11-29 11:10:58 24 4
gpt4 key购买 nike

我正在尝试在 Postgres 9.4 的 Postgres jsonb 字段中保留如下值:

[{"event_slug":"test_1","start_time":"2014-10-08","end_time":"2014-10-12"},
{"event_slug":"test_2","start_time":"2013-06-24","end_time":"2013-07-02"},
{"event_slug":"test_3","start_time":"2014-03-26","end_time":"2014-03-30"}]

我正在执行如下查询:

SELECT * FROM locations
WHERE EXISTS (
SELECT 1 FROM jsonb_array_elements(events) AS e
WHERE (
e->>'event_slug' = 'test_1' AND
(
e->>'start_time' >= '2014-10-30 14:04:06 -0400' OR
e->>'end_time' >= '2014-10-30 14:04:06 -0400'
)
)
)

我如何为该数据创建索引以供上述查询使用?对于每列包含约 10 个事件的几百万行,这听起来合理吗?

值得注意的是,我似乎仍在使用以下命令进行顺序扫描:

CREATE INDEX events_gin_idx ON some_table USING GIN (events);

我猜这是因为我在查询中做的第一件事是将数据转换为 json 数组元素。

最佳答案

首先,您不能像那样访问 JSON 数组值。对于给定的 json 值:

[{"event_slug":"test_1","start_time":"2014-10-08","end_time":"2014-10-12"},
{"event_slug":"test_2","start_time":"2013-06-24","end_time":"2013-07-02"},
{"event_slug":"test_3","start_time":"2014-03-26","end_time":"2014-03-30"}]

针对第一个数组元素的有效测试是:

WHERE e->0->>'event_slug' = 'test_1'

但您可能不想将搜索限制在数组的第一个元素。随着jsonb Postgres 9.4 中的数据类型,您有额外的运算符和索引支持。要索引数组的元素,您需要一个 GIN 索引。

GIN 索引的内置运算符类不支持“大于”或“小于”运算符 > >= < <= 。这适用于 jsonb同样,您可以在两个运算符类之间进行选择。 The manual :

Name             Indexed Data Type  Indexable Operators
...
jsonb_ops jsonb ? ?& ?| @>
jsonb_path_ops jsonb @>

(jsonb_ops 是默认值。)您可以涵盖相等性测试,但这些运算符均未涵盖您对 >= 的要求。比较。你需要一个 btree 索引。

基本解决方案

使用索引支持相等性检查:

CREATE INDEX locations_events_gin_idx ON locations
USING gin (events jsonb_path_ops);

SELECT * FROM locations WHERE events @> '[{"event_slug":"test_1"}]';

如果过滤器有足够的选择性,这可能就足够了。
假设 end_time >= start_time ,所以我们不需要两次检查。仅检查 end_time更便宜且等效:

SELECT l.*
FROM locations l
, jsonb_array_elements(l.events) e
WHERE l.events @> '[{"event_slug":"test_1"}]'
AND (e->>'end_time')::timestamp >= '2014-10-30 14:04:06 -0400'::timestamptz;

利用隐式 JOIN LATERAL .详情(上一章):

注意不同的数据类型! JSON 值中的内容类似于 timestamp [without time zone] ,而您的谓词使用 timestamp with time zone文字。 timestamp值根据当前的时区 设置进行解释,而给定的 timestamptz文字必须转换为 timestamptz明确或时区将被忽略!以上查询应按需要工作。详细解释:

关于 jsonb_array_elements() 的更多解释:

高级解决方案

如果以上还不够好,我会考虑 MATERIALIZED VIEW 以规范化形式存储相关属性。这允许普通的 btree 索引。

代码假定您的 JSON 值具有与问题中显示的一致的格式。

设置:

CREATE TYPE event_type AS (
, event_slug text
, start_time timestamp
, end_time timestamp
);

CREATE MATERIALIZED VIEW loc_event AS
SELECT l.location_id, e.event_slug, e.end_time -- start_time not needed
FROM locations l, jsonb_populate_recordset(null::event_type, l.events) e;

jsonb_populate_recordset() 的相关答案:

CREATE INDEX loc_event_idx ON loc_event (event_slug, end_time, location_id);

还包括location_id允许仅索引扫描。 (参见 manual pagePostgres Wiki。)

查询:

SELECT *
FROM loc_event
WHERE event_slug = 'test_1'
AND end_time >= '2014-10-30 14:04:06 -0400'::timestamptz;

或者,如果您需要来自底层 locations 的完整行表:

SELECT l.*
FROM (
SELECT DISTINCT location_id
FROM loc_event
WHERE event_slug = 'test_1'
AND end_time >= '2014-10-30 14:04:06 -0400'::timestamptz
) le
JOIN locations l USING (location_id);

关于sql - 在 Postgres jsonb 中查询数组结构的正确索引是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26499266/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com