PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Chỉ mục thích hợp để truy vấn cấu trúc trong mảng trong Postgres jsonb là gì?

Trước hết, bạn không thể truy cập các giá trị mảng JSON như vậy. Đối với một giá trị json đã cho

[{"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"}]

Kiểm tra hợp lệ đối với phần tử mảng đầu tiên sẽ là:

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

Nhưng có thể bạn không muốn giới hạn tìm kiếm của mình ở phần tử đầu tiên của mảng. Với jsonb kiểu dữ liệu trong Postgres 9.4 bạn có thêm toán tử và hỗ trợ chỉ mục. Để lập chỉ mục các phần tử của một mảng, bạn cần có chỉ mục GIN.

Các lớp toán tử cài sẵn cho chỉ mục GIN không hỗ trợ toán tử "lớn hơn" hoặc "nhỏ hơn" > >= < <= . Điều này đúng với jsonb cũng như, nơi bạn có thể chọn giữa hai lớp toán tử. Theo tài liệu:

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

(jsonb_ops là mặc định.) Bạn có thể bao gồm kiểm tra bình đẳng, nhưng cả hai toán tử đó đều không bao gồm yêu cầu của bạn đối với >= sự so sánh. Bạn sẽ cần một chỉ mục btree.

Giải pháp cơ bản

Để hỗ trợ việc kiểm tra sự bình đẳng với một chỉ mục:

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

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

Điều này có thể đủ tốt nếu bộ lọc đủ chọn lọc.
Giả sử end_time >= start_time , vì vậy chúng tôi không cần hai lần kiểm tra. Chỉ kiểm tra end_time rẻ hơn và tương đương:

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;

Sử dụng JOIN LATERAL ngầm . Chi tiết (chương cuối):

  • PostgreSQL unnest () với số phần tử

Cẩn thận với các loại dữ liệu khác nhau ! Những gì bạn có trong giá trị JSON trông giống như timestamp [without time zone] , trong khi các vị từ của bạn sử dụng timestamp with time zone nghĩa đen. Dấu thời gian timestamp giá trị được diễn giải theo múi giờ hiện tại trong khi timestamptz đã cho các chữ phải được truyền thành timestamptz rõ ràng hoặc múi giờ sẽ bị bỏ qua! Truy vấn trên sẽ hoạt động như mong muốn. Giải thích chi tiết:

  • Bỏ qua hoàn toàn các múi giờ trong Rails và PostgreSQL

Giải thích thêm cho jsonb_array_elements() :

  • Tham gia PostgreSQL bằng JSONB

Giải pháp nâng cao

Nếu điều trên không đủ tốt, tôi sẽ xem xét một MATERIALIZED VIEW lưu trữ các thuộc tính có liên quan ở dạng chuẩn hóa. Điều này cho phép lập chỉ mục btree đơn giản.

Mã giả định rằng các giá trị JSON của bạn có định dạng nhất quán như được hiển thị trong câu hỏi.

Thiết lập:

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;

Câu trả lời liên quan cho jsonb_populate_recordset() :

  • Cách chuyển đổi kiểu jsonb của PostgreSQL 9.4 thành float
CREATE INDEX loc_event_idx ON loc_event (event_slug, end_time, location_id);

Cũng bao gồm location_id để cho phép chỉ quét chỉ mục . (Xem trang hướng dẫn sử dụng và Postgres Wiki.)

Truy vấn:

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

Hoặc, nếu bạn cần các hàng đầy đủ từ các vị trí timestamp bên dưới bảng:

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);


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách thay đổi kiểu viền bảng trong kết quả truy vấn psql

  2. Phá hủy một Postgres DB trên Heroku

  3. Truy vấn một tham số (cài đặt postgresql.conf) như max_connections

  4. Cài đặt Postgres trên windows để sử dụng với Ruby-on-Rails

  5. Tôi muốn tìm nạp dữ liệu từ các tên bảng khác nhau bằng cách sử dụng hàm postgresql