查询包含 JSON 个对象数组的 jsonb 列
Query jsonb column containing array of JSON objects
我使用 PostgreSQL 9.5 和 Rails 5. 我想查询下面显示的 jsonb
列,它包含一个数组JSON 反对 return 所有包含 {"kind":"person"}
的 JSON 数组元素并执行计数。
我使用的 SQL 显示在 json 数据下方。 运行 查询只是 return 一个空数组。
我已经尝试了建议的查询 here and here。
这是我的 jsonb
数据的样子:
'[
{"kind":"person", "filter_term":"56","selected_attr":"customer"},
{"kind":"email", "filter_term":"marketer","selected_attr":"job_title"}
]'
我想要 sql 查询之一到 return:
data
----------------------------------------------------------------------
'{"kind":"person", "filter_term":"56","selected_attr":"customer"}'
(1 row)
以及对 return 数组返回的另一个查询,以便我可以在我的应用程序中调用它并循环遍历它以创建表单:
data
----------------------------------------------------------------------
'[{"kind":"person", "filter_term":"56","selected_attr":"customer"}]'
(1 row)
我试过这个 SQL 查询:
"SELECT * FROM \"segments\" WHERE (payload @> '[{\"kind\":\"person\"}]')"
我也试过这个查询:
"SELECT payload FROM segments WHERE payload @> '[{\"kind\":\"person\"}]'::jsonb;"
这是第三个查询:
"SELECT * FROM segments s WHERE s.payload->'\"#{a}\"' @> '[{\"kind\":\"person\"}]';"
型号:
class Segment < ApplicationRecord
store_accessor :payload,:kind, :filter_term, :selected_model_name, :selected_attr, :limit, :selected_operator
end
迁移:
create_table "segments", force: :cascade do |t|
t.jsonb "payload", default: "[]", null: false
t.index ["payload"], name: "index_segments_on_payload", using: :gin
end
假设这个 table 定义:
CREATE TABLE segments (segments_id serial PRIMARY KEY, payload jsonb);
具有这样的 JSON 值:
INSERT INTO segments (payload)
VALUES ('[
{
"kind": "person",
"limit": "1",
"filter_term": "56",
"selected_attr": "customer",
"selected_operator": "less_than"
},
{
"kind": "email",
"filter_term": "marketer",
"selected_attr": "job_title",
"selected_operator": "equals"
}
]'
);
- 您想要 return 包含 key/value 对
"kind":"person"
的 JSON 数组的元素(不是嵌套的 JSON 对象 {"kind":"person"}
) - 并计算数组元素以及 table 行(每行可能有多个匹配的数组元素)。
解决方案
获取 行数 包含 segments
列中符合条件的 jsonb
值:
SELECT count(*)
FROM segments s
WHERE s.payload @> '[{"kind":"person"}]';
获取所有符合条件的JSON数组元素(即JSON对象本身)-加上总数元素的数量(可能同时大于上述数量:
SELECT j.*
FROM segments s
JOIN LATERAL jsonb_array_elements(s.payload) j(elem) ON j.elem @> '{"kind":"person"}'
WHERE s.payload @> '[{"kind":"person"}]';
Returns:
elem
------------------------------------------------------------
{"kind": "person", "limit": "1", "filter_term": "56", ... }
一次获得:
SELECT j.*, count(*) OVER () AS ct_elem, s.ct_rows
FROM (
SELECT payload, count(*) OVER () AS ct_rows
FROM segments
WHERE payload @> '[{"kind":"person"}]'
) s
JOIN LATERAL jsonb_array_elements(s.payload) j(elem) ON j.elem @> '{"kind":"person"}';
Returns(对于具有更多条目的 table):
elem | ct_elem | ct_rows
--------------------------+---------+---------
{"kind": "person", ... } | 4 | 3
{"kind": "person", ... } | 4 | 3
...
但是我觉得你真的很想要这个:
SELECT a.*
, sum(ct_elem_row) OVER () AS ct_elem_total
, count(*) OVER () AS ct_rows
FROM segments s
JOIN LATERAL (
SELECT json_agg(j.elem) AS filtered_payload, count(*) AS ct_elem_row
FROM jsonb_array_elements(s.payload) j(elem)
WHERE j.elem @> '{"kind":"person"}'
) a ON ct_elem_row > 0
WHERE s.payload @> '[{"kind":"person"}]';
Returns(对于具有更多条目的 table):
filtered_payload | ct_elem_row | ct_elem_total | ct_rows
-----------------------------------------------------+-------------+---------------+---------
[{"kind": "person", ... }] | 1 | 4 | 3
[{"kind": "person", ... }] | 1 | 4 | 3
[{"kind": "person", ... }, {"kind": "person", ... }] | 2 | 4 | 3
这会识别匹配的行,然后 select 匹配数组元素,并为每行构建一个仅包含这些元素的数组。加上计数。
为了获得最佳性能,您需要一个 jsonb_path_ops
GIN 索引,例如:
CREATE INDEX segments_path_ops_gin_idx ON segments
USING gin (payload jsonb_path_ops);
(但服务于更多不同查询的更通用的索引可能是更好的选择。)
相关:
Index for finding an element in a JSON array
Query for array elements inside JSON type
Best way to get result count before LIMIT was applied
术语
我们正在处理一个 JSON 对象 包含 一个 JSON 数组,保存为 Postgres jsonb
数据类型 - a "JSON 数组”简称,但 不是 一个“JSON 的数组”。
我使用 PostgreSQL 9.5 和 Rails 5. 我想查询下面显示的 jsonb
列,它包含一个数组JSON 反对 return 所有包含 {"kind":"person"}
的 JSON 数组元素并执行计数。
我使用的 SQL 显示在 json 数据下方。 运行 查询只是 return 一个空数组。
我已经尝试了建议的查询 here and here。
这是我的 jsonb
数据的样子:
'[
{"kind":"person", "filter_term":"56","selected_attr":"customer"},
{"kind":"email", "filter_term":"marketer","selected_attr":"job_title"}
]'
我想要 sql 查询之一到 return:
data
----------------------------------------------------------------------
'{"kind":"person", "filter_term":"56","selected_attr":"customer"}'
(1 row)
以及对 return 数组返回的另一个查询,以便我可以在我的应用程序中调用它并循环遍历它以创建表单:
data
----------------------------------------------------------------------
'[{"kind":"person", "filter_term":"56","selected_attr":"customer"}]'
(1 row)
我试过这个 SQL 查询:
"SELECT * FROM \"segments\" WHERE (payload @> '[{\"kind\":\"person\"}]')"
我也试过这个查询:
"SELECT payload FROM segments WHERE payload @> '[{\"kind\":\"person\"}]'::jsonb;"
这是第三个查询:
"SELECT * FROM segments s WHERE s.payload->'\"#{a}\"' @> '[{\"kind\":\"person\"}]';"
型号:
class Segment < ApplicationRecord
store_accessor :payload,:kind, :filter_term, :selected_model_name, :selected_attr, :limit, :selected_operator
end
迁移:
create_table "segments", force: :cascade do |t|
t.jsonb "payload", default: "[]", null: false
t.index ["payload"], name: "index_segments_on_payload", using: :gin
end
假设这个 table 定义:
CREATE TABLE segments (segments_id serial PRIMARY KEY, payload jsonb);
具有这样的 JSON 值:
INSERT INTO segments (payload)
VALUES ('[
{
"kind": "person",
"limit": "1",
"filter_term": "56",
"selected_attr": "customer",
"selected_operator": "less_than"
},
{
"kind": "email",
"filter_term": "marketer",
"selected_attr": "job_title",
"selected_operator": "equals"
}
]'
);
- 您想要 return 包含 key/value 对
"kind":"person"
的 JSON 数组的元素(不是嵌套的 JSON 对象{"kind":"person"}
) - 并计算数组元素以及 table 行(每行可能有多个匹配的数组元素)。
解决方案
获取 行数 包含 segments
列中符合条件的 jsonb
值:
SELECT count(*)
FROM segments s
WHERE s.payload @> '[{"kind":"person"}]';
获取所有符合条件的JSON数组元素(即JSON对象本身)-加上总数元素的数量(可能同时大于上述数量:
SELECT j.*
FROM segments s
JOIN LATERAL jsonb_array_elements(s.payload) j(elem) ON j.elem @> '{"kind":"person"}'
WHERE s.payload @> '[{"kind":"person"}]';
Returns:
elem ------------------------------------------------------------ {"kind": "person", "limit": "1", "filter_term": "56", ... }
一次获得:
SELECT j.*, count(*) OVER () AS ct_elem, s.ct_rows
FROM (
SELECT payload, count(*) OVER () AS ct_rows
FROM segments
WHERE payload @> '[{"kind":"person"}]'
) s
JOIN LATERAL jsonb_array_elements(s.payload) j(elem) ON j.elem @> '{"kind":"person"}';
Returns(对于具有更多条目的 table):
elem | ct_elem | ct_rows --------------------------+---------+--------- {"kind": "person", ... } | 4 | 3 {"kind": "person", ... } | 4 | 3 ...
但是我觉得你真的很想要这个:
SELECT a.*
, sum(ct_elem_row) OVER () AS ct_elem_total
, count(*) OVER () AS ct_rows
FROM segments s
JOIN LATERAL (
SELECT json_agg(j.elem) AS filtered_payload, count(*) AS ct_elem_row
FROM jsonb_array_elements(s.payload) j(elem)
WHERE j.elem @> '{"kind":"person"}'
) a ON ct_elem_row > 0
WHERE s.payload @> '[{"kind":"person"}]';
Returns(对于具有更多条目的 table):
filtered_payload | ct_elem_row | ct_elem_total | ct_rows -----------------------------------------------------+-------------+---------------+--------- [{"kind": "person", ... }] | 1 | 4 | 3 [{"kind": "person", ... }] | 1 | 4 | 3 [{"kind": "person", ... }, {"kind": "person", ... }] | 2 | 4 | 3
这会识别匹配的行,然后 select 匹配数组元素,并为每行构建一个仅包含这些元素的数组。加上计数。
为了获得最佳性能,您需要一个 jsonb_path_ops
GIN 索引,例如:
CREATE INDEX segments_path_ops_gin_idx ON segments
USING gin (payload jsonb_path_ops);
(但服务于更多不同查询的更通用的索引可能是更好的选择。)
相关:
Index for finding an element in a JSON array
Query for array elements inside JSON type
Best way to get result count before LIMIT was applied
术语
我们正在处理一个 JSON 对象 包含 一个 JSON 数组,保存为 Postgres jsonb
数据类型 - a "JSON 数组”简称,但 不是 一个“JSON 的数组”。