测试Postgres json数组中的项目成员资格

时间:2015-01-12 23:51:11

标签: postgresql postgresql-9.3

假设我在Postgres中有一张看起来像这样的表 - 注意拉链字段是json。

cities
name (text)    | zips (json)
San Francisco  | [94100, 94101, ...]
Washington DC  | [20000, 20001, ...]

现在我想做select * from cities where zip=94101之类的事情,换句话说,就是测试会员资格。

我尝试使用WHERE zips ? '94101'并获得operator does not exist: json ? unknown

我尝试使用WHERE zips->'94101',但不确定要放在那里,因为Postgres抱怨argument of WHERE must by type boolean, not type json

我想要什么?我如何解决9.3和9.4?

编辑是的,我知道我应该使用本机数组类型...我们使用的数据库适配器不支持此功能。

3 个答案:

答案 0 :(得分:4)

在PostgreSQL 9.4+中,您可以将@>运算符与jsonb类型一起使用:

create table test (city text, zips jsonb);
insert into test values ('A', '[1, 2, 3]'), ('B', '[4, 5, 6]');
select * from test where zips @> '[1]';

这种方法的另一个优点是9.4的新GIN indexes加速了对大桌面的查询。

答案 1 :(得分:2)

对于PostgreSQL 9.4+,您应该使用json[b]_array_elements_text()
(包含运算符?执行类似的操作,但对于JSON数组,它只能找到完全匹配,如果您的数组包含字符串而不是数字,则只能发生匹配)

create table cities (
  city text,
  zips jsonb
);

insert into cities (city, zips) values
    ('Test1', '[123, 234]'),
    ('Test2', '[234, 345]'),
    ('Test3', '[345, 456]'),
    ('Test4', '[456, 123]'),
    ('Test5', '["123", "note the quotes!"]'),
    ('Test6', '"123"'), -- this is a string in json(b)
    ('Test7', '{"123": "this is an object, not an array!"}');

-- select * from cities where zips ? '123';
-- would yield 'Test5', 'Test6' & 'Test7', but none of you want

-- this is a safe solution:
select cities.*
from   cities
join   jsonb_array_elements_text(
         case jsonb_typeof(zips)
           when 'array' then zips
           else '[]'
         end
       ) zip on zip = '123';

-- but you can use this simplified query, if you are sure,
-- your "zips" column only contains JSON arrays:
select cities.*
from   cities
join   jsonb_array_elements_text(zips) zip on zip = '123';

对于9.3,您可以使用json_array_elements()(并手动将拉链转换为text):

select cities.*
from   cities
join   json_array_elements(zips) zip on zip::text = '123';

注意:对于9.3,您无法使查询安全(至少很容易),您只需要在zips列中存储JSON数组。此外,上面的查询将找不到任何字符串匹配,您的数组元素需要是数字。

注释2 :对于9.4+,您也可以使用json的安全解决方案(不仅仅是jsonb,但您必须拨打json_typeof(zips)而不是jsonb_typeof(zips))。

编辑:实际上,@>运算符在PostgreSQL 9.4+中更好,为@Ainar-G mentioned(因为它是可索引的)。一点注意事项:如果你的列和查询都使用JSON数字(或JSON字符串,但没有混合),它只会找到行。

答案 2 :(得分:1)

对于9.3,您可以使用json_array_elements()。我现在无法在版本9.4中使用jsonb进行测试。

create table test (
  city varchar(35) primary key,
  zips json not null
);

insert into test values
('San Francisco', '[94101, 94102]');

select * 
from (
  select *, json_array_elements(zips)::text as zip from test
) x 
where zip = '94101';