如何在PostgreSQL 9.4+中将一个简单的json(b)int数组转换为整数[]

时间:2015-01-31 23:17:34

标签: sql arrays json postgresql types

我有一个来自json对象的数组:[1, 9, 12]

由于它使用方括号表示法,因为它是直接从json对象获取的,我无法将其强制转换为::integer[],当我尝试使用array_agg(jsonb_array_elements(simpleintarray))时,我收到错误消息,说我需要分组id,但由于数组不是对象(键/值)对,而只是简单的整数,我无法看到如何以一种相当有效的方式实现这一点。

从json返回上述简单int数组的查询是:

SELECT node.*, elem->'permissions' AS group_node_permissions
    FROM node
    LEFT OUTER JOIN
    jsonb_array_elements(my_user_group.node_permissions) elem
    ON elem->>'id' = node.id::text
    ORDER BY node.id

elem->'permissions'理想情况下应以{}格式返回Postgres数组,以便稍后使用ANY(intarray)函数。

我希望避免执行多余的低效解决方法,例如将elem->'permissions'转换为字符串,->>用大括号替换方括号,然后转换为整数数组,尽管可能会工作

在伪代码中,我真正需要的是能够得到相同的结果:

SELECT node.*, elem->'permissions'**::integer[]** AS group_node_permissions,

...但当然由于从json数组到PostgreSQL数组格式的[] vs {}不同,这将导致错误。

这是我目前的(非常难看的解决方案):

SELECT node.*, replace(replace(elem->>'permissions', '[', '{'),']','}')::integer[] AS group_node_permissions

它将原始[1, 9, 12](jsonb)转换为{1,9,12}形式(整数[])

有没有更好的解决方案?

P.S。

是否值得从json(b)转换为int数组([]),您可以使用jsonarray @> '12'将数组元素提取到Postgres integer[]数组,您可以在其中使用{{ 1}}。有没有人知道哪个性能更高,应该更好地扩展?现在我们可以将数组放在12 = ANY(intarray)数据类型的列中,这被认为是优选的方式,例如。 jsonb数据类型?


扩展信息(根据Erwin的要求):

integer[]

DDL:

SELECT DISTINCT ON (my_node.id) my_node.*
FROM user_group AS my_user_group,
LATERAL
(
    SELECT node.*, elem->'permissions' AS user_group_node_permissions
    FROM node
    LEFT OUTER JOIN
    jsonb_array_elements(my_user_group.node_permissions) elem
    ON elem->>'id' = node.id::text
    ORDER BY node.id
)my_node
WHERE (my_user_group.id = ANY('{2,3}')) --try also with just: ANY('{3}')) to see node 3 is excluded
AND (user_group_node_permissions @> '12' OR (user_group_node_permissions IS NULL AND 12 = ANY(my_user_group.default_node_permissions)));

DML:

节点:

CREATE TABLE node
(
  id bigserial NOT NULL,
  path ltree,
  name character varying(255),
  node_type smallint NOT NULL,
  created_by bigint NOT NULL,
  created_date timestamp without time zone NOT NULL DEFAULT now(),
  parent_id bigint,
  CONSTRAINT node_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

CREATE TABLE user_group
(
  id serial NOT NULL,
  name character varying,
  alias character varying,
  node_permissions jsonb,
  section_ids jsonb,
  default_node_permissions jsonb
)
WITH (
  OIDS=FALSE
);

组:

INSERT INTO node VALUES (1, '1', 'root', 5, 1, '2014-10-22 16:51:00.215', NULL);

INSERT INTO node VALUES (2, '1.2', 'Home', 1, 1, '2014-10-22 16:51:00.215', 1);
INSERT INTO node VALUES (3, '1.2.3', 'Sample Page', 1, 1, '2014-10-22 16:51:00.215', 2);
INSERT INTO node VALUES (4, '1.2.3.4', 'Child Page Level 1', 1, 1, '2014-10-26 23:19:44.735', 3);
INSERT INTO node VALUES (5, '1.2.3.4.5', 'Child Page Level 2', 1, 1, '2014-10-26 23:19:44.735', 4);

INSERT INTO node VALUES (6, '1.2.6', 'Test Page', 1, 1, '2014-12-01 11:45:16.186', 2);
INSERT INTO node VALUES (7, '1.2.7', 'Login', 1, 1, '2014-12-01 11:54:10.208', 2);
INSERT INTO node VALUES (8, '1.2.7.8', 'MySubPage', 1, 1, '2014-12-01 12:02:54.252', 7);
INSERT INTO node VALUES (9, '1.2.9', 'Yet another test page', 1, 1, '2014-12-01 12:07:29.999', 2);

INSERT INTO node VALUES (10, '1.2.10', 'Testpage 2', 1, 1, '2014-12-02 01:43:33.233', 2);
INSERT INTO node VALUES (11, '1.2.10.11', 'Test page 2 child', 1, 1, '2014-12-02 01:45:49.78', 10);

简短说明:

基本上我在这里做的是:

  • 用户可以拥有多个组(作为integer []数据类型或jsonb数组[] - 尚未确定,但考虑到Erwin的答案,整数可能是最好的,因为它不应该持有大阵列。
  • 可以为每个组分配对特定节点的特定访问权限*(参见下图,其中说明了左外连接),从而覆盖了组默认的全局权限(权限" 12"是否具有浏览功能节点,从而在查询中返回它)
  • 自"作家"小组确实有" 12" (浏览)权限,但是具有id 3的节点的节点权限没有权限" 12",只有组" writer"不会在select查询返回的结果中获取id为3的节点。但是,如果用户还有另一个组,并且不排除该节点 - 当然会返回该节点,因为更多"功能强大"团体取代了“弱”"的。

效果不佳 - 可以优化吗?

EXPLAIN ANALYZE query performance

Text output

(您可以放大浏览器中的图片)

与上述相比,一个简单的SELECT * FROM节点在0.046ms内执行(再次使用EXPLAIN ANALYZE测量)

如果您仍然可以使用更多信息,请随时询问。

1 个答案:

答案 0 :(得分:3)

显然,你有一个嵌套在外部JSON数组中的JSON数组:

SELECT n.*, array_agg(p)::int[] AS group_node_permissions
FROM   my_user_group u
     , jsonb_array_elements(u.node_permissions) elem
JOIN   node n ON n.id = (elem->>'id')::int
     , jsonb_array_elements_text(elem->'permissions') p
GROUP  BY n.id;  -- id being the PK

关于dba.SE的相关答案以及更多细节和解释:

根据用例的详细信息,使用GIN索引支持查询可能是个好主意:

至于你的 P.S。,这取决于完整的图片。除了Postgres数组之外的所有其他考虑因素通常比持有JSON数组的jsonb小一点且更快。使用GIN索引可以非常快速地测试元素是否存在:

jsonarray @> '12'
intarray @> '{12}'

请特别注意,GIN索引支持变种12 = ANY(intarray) Details in the manual.