sql – 在Postgres jsonb中查询数组中的结构的正确索引是什么?
在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","end_time":"2014-03-30"}] 对第一个数组元素的有效测试将是: WHERE e->0->>'event_slug' = 'test_1' 但是您可能不想将搜索限制在数组的第一个元素中.使用Postgres 9.4中的jsonb数据类型,您可以使用其他操作符和索引支持.要索引数组的元素,您需要一个GIN索引. GIN索引的内置操作符类不支持“大于”或“小于”运算符> > =< < =.这也适用于jsonb,您可以在两个运算符类之间进行选择. Per documentation: 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"}]'; 如果过滤器具有足够的选择性,这可能是足够好的. 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.细节(最后一章): > PostgreSQL unnest() with element number 小心不同的数据类型! JSON值中的内容看起来像时间戳[没有时区],而你的谓词使用时区文字的时间戳.时间戳值根据当前时区设置进行解释,而给定的timestamptz文字必须明确地转换为timestamptz,否则时区将被忽略!以上查询应按要求工作.详细说明: > Ignoring timezones altogether in Rails and PostgreSQL jsonb_array_elements()的更多解释: > PostgreSQL joining using JSONB 高级解决方案 如果上述不够好,我会考虑一个 该代码假定您的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()的相关答案: > How to convert PostgreSQL 9.4’s jsonb type to float CREATE INDEX loc_event_idx ON loc_event (event_slug,end_time,location_id); 还包括location_id以允许仅索引扫描. (见manual page和Postgres Wiki) 查询: SELECT * FROM loc_event WHERE event_slug = 'test_1' AND end_time >= '2014-10-30 14:04:06 -0400'::timestamptz; 或者,如果您需要从底层的位置表完整的行: 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); (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |