2

我(仍然)是 postgresql 和 jsonb 的新手。我正在尝试从子查询中选择一些记录并被卡住。我的数据列如下所示(jsonb):

{"people": [{"age": "50", "name": "Bob"}], "another_key": "no"}
{"people": [{"age": "73", "name": "Bob"}], "another_key": "yes"}

这是我的查询。我想选择年龄大于 30 岁的“Bob”的所有名字:

SELECT * FROM mytable
WHERE (SELECT (a->>'age')::float
       FROM (SELECT jsonb_array_elements(data->'people') as a
             FROM mytable) as b 
       WHERE a @> json_object(ARRAY['name', 'Bob'])::jsonb
      ) > 30;

我得到错误:

more than one row returned by a subquery used as an expression

我不太明白。如果我做一些简单的替换(仅用于测试),我可以这样做:

SELECT * FROM mytable
WHERE  (50) > 30  -- 50 is the age of the youngest Bob

并返回两行。

4

3 回答 3

3

该错误意味着它所说的:

用作表达式的子查询返回的不止一行

子句中的表达式WHERE需要一个值(就像您在添加的测试中替换一样),但您的子查询返回行。jsonb_array_elements()是一个集合返回函数。

假设这个表定义:

CREATE TABLE mytable (
  id   serial PRIMARY KEY
, data jsonb
);

"people"如果里面不能有多个人,那么JSON 数组就没有意义了。你的例子只有一个人是误导性的。一些更具启发性的测试数据:

INSERT INTO mytable (data)
VALUES
  ('{"people": [{"age": "55", "name": "Bill"}], "another_key": "yes"}')
, ('{"people": [{"age": "73", "name": "Bob"}], "another_key": "yes"}')
, ('{"people": [{"age": "73", "name": "Bob"}
               ,{"age": "77", "name": "Udo"}], "another_key": "yes"}');

第三排有两个人。

我建议使用LATERAL连接查询:

SELECT t.id, p.person
FROM   mytable t 
     , jsonb_array_elements(t.data->'people') p(person)  -- implicit LATERAL
WHERE (t.data->'people') @> '[{"name": "Bob"}]'
AND    p.person->>'name' = 'Bob'
AND   (p.person->>'age')::int > 30;

第一个WHERE条件WHERE (t.data->'people') @> '[{"name": "Bob"}]'在逻辑上是多余的,但它通过及早消除不相关的行来提高性能:甚至不要取消嵌套没有 a"Bob"的 JSON 数组。

对于大表,使用匹配索引会更有效如果你定期运行这种查询,你应该有一个:

CREATE INDEX mytable_people_gin_idx ON mytable
USING gin ((data->'people') jsonb_path_ops);

相关,有更多解释:

于 2015-06-17T02:01:17.437 回答
1

在您的子查询中:

SELECT (a->>'age')::float
FROM (SELECT jsonb_array_elements(data->'people') as a
      FROM mytable) as b
WHERE a @> json_object(ARRAY['name', 'Bob'])::jsonb

你重新选择了所有的行mytable。这就是您的子查询返回多个值的原因。

如果您想从包含满足某些条件的元素的表中选择行,那么在您的条件中,不要从该表中重新选择;使用您在外部查询中已经选择的行:

SELECT * FROM mytable
WHERE exists(SELECT 1
    FROM (SELECT jsonb_array_elements(data->'people') as person) as x
    WHERE person @> '{"name": "Bob"}'
    AND (person->>'age')::float > 30)

据我所知,这种奇怪的双子查询语法是必要的。请注意,这data是来自外部查询。

如果您想从"people"满足条件的字段"people"中选择所有 JSON 对象,则只需聚合所有这些元素并过滤它们:

SELECT person
FROM (SELECT jsonb_array_elements(data->'people') as person
      FROM mytable) as x
WHERE person @> '{"name": "Bob"}'
AND (person->>'age')::float > 30
于 2015-06-16T05:05:34.227 回答
-1

您的示例的正确查询如下:

SELECT * 
FROM mytable
WHERE (data  #> '{people,0}' ->>'name') = 'Bob' 
AND (data  #> '{people,0}' ->>'age')::integer > 30

(注意“people”的值是一个数组)。

于 2015-06-16T05:01:50.863 回答