繁体   English   中英

如何在PostgreSQL中获取数组值的索引?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM