[英]How to get index of an array value in PostgreSQL?
我有一个像这样的pins
表:
id (int) | pin_codes (jsonb)
--------------------------------
1 | [4000, 5000, 6000]
2 | [8500, 8400, 8600]
3 | [2700, 2300, 2980]
现在,我想要使用pin_code 8600
及其数组索引的行。 输出必须如下:
pin_codes | index
------------------------------
[8500, 8500, 8600] | 2
如果我想要pin_code 2700
的行,输出:
pin_codes | index
------------------------------
[2700, 2300, 2980] | 0
到目前为止我尝试过的:
SELECT pin_codes FROM pins WHERE pin_codes @> '[8600]'
它只返回具有所需值的行。 我不知道如何获取pin_codes数组中的值的索引!
任何帮助将非常感谢。
PS:
我正在使用PostgreSQL 10
使用函数jsonb_array_elements_text()
with ordinality.
with my_table(id, pin_codes) as (
values
(1, '[4000, 5000, 6000]'::jsonb),
(2, '[8500, 8400, 8600]'),
(3, '[2700, 2300, 2980]')
)
select id, pin_codes, ordinality- 1 as index
from my_table, jsonb_array_elements_text(pin_codes) with ordinality
where value::int = 8600;
id | pin_codes | index
----+--------------------+-------
2 | [8500, 8400, 8600] | 2
(1 row)
如果您将数组存储为真实数组而不是json,则可以使用array_position()
来查找给定元素的(第一个)索引:
select array_position(array['one', 'two', 'three'], 'two')
返回2
通过一些文本修改,您可以将JSON数组转换为文本数组:
select array_position(translate(pin_codes::text,'[]','{}')::text[], '8600')
from the_table;
还允许你使用“操作员”
select *
from pins
where '8600' = any(translate(pin_codes::text,'[]','{}')::text[])
contains @>
运算符需要运算符两侧的数组。 您可以使用它一次搜索两个密码:
select *
from pins
where translate(pin_codes::text,'[]','{}')::text[] @> array['8600','8400']
或者使用重叠运算符&&
查找包含多个元素的行:
select *
from pins
where translate(pin_codes::text,'[]','{}')::text[] && array['8600','2700']
会回来的
id | pin_codes
---+-------------------
2 | [8500, 8400, 8600]
3 | [2700, 2300, 2980]
如果你这么做,那么将pin_codes存储为text[]
而不是JSON会更高效 - 那么你也可以索引该列以更有效地进行搜索。
如前所述,array_position函数仅在Postgres 9.5及更高版本中可用。
这是自定义函数,实现相同,源自github上的nathansgreen。
-- The array_position function was added in Postgres 9.5.
-- For older versions, you can get the same behavior with this function.
create function array_position(arr ANYARRAY, elem ANYELEMENT, pos INTEGER default 1) returns INTEGER
language sql
as $BODY$
select row_number::INTEGER
from (
select unnest, row_number() over ()
from ( select unnest(arr) ) t0
) t1
where row_number >= greatest(1, pos)
and (case when elem is null then unnest is null else unnest = elem end)
limit 1;
$BODY$;
所以在这种特殊情况下,创建函数后,以下对我有用。
SELECT
pin_codes,
array_position(pin_codes, 8600) AS index
FROM pins
WHERE array_position(pin_codes, 8600) IS NOT NULL;
值得注意的是,它只会返回第一次出现的8600的索引,您可以使用pos参数来索引您喜欢的事件。
简而言之,规范化您的数据结构,或者不要在SQL中执行此操作。 如果您希望给定当前数据结构的子数据元素的索引,那么在您的应用程序代码中执行此操作(获取结果,转换为列表/数组,获取索引)。
尝试取消字符串并分配数字,如下所示:
with dat as
(
select 1 id, '8700, 5600, 2300' pins
union all
select 2 id, '2300, 1700, 1000' pins
)
select dat.*, t.rn as index
from
(
select id, t.pins, row_number() over (partition by id) rn
from
(
select id, trim(unnest(string_to_array(pins, ','))) pins from dat
) t
) t
join dat on dat.id = t.id and t.pins = '2300'
如果你坚持存储数组,我会推迟克林斯回答。
作为我的评论的替代答案和扩展......不要将SQL数据存储在数组中。 提前“规范化”您的数据,SQL将更好地处理它。 Klin的答案很好,但可能因性能而受到影响,因为它超出了SQL最擅长的范围。
我在存储之前打破了数组。 如果已知pincodes的数量,那么简单地将表pin_id,pin1,pin2,pin3,pinetc ......起作用。
如果引脚数未知,则第一个表作为存储pin_id的引脚和与该引脚ID相关的任何信息列,然后第二个表作为pin_id,pin_seq,pin_value也可以使用(尽管您可能需要稍后将其转换了解数据)。 在这种情况下,选择pin_seq,其中pin_value = 260将起作用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.