简体   繁体   English

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

[英]How to get index of an array value in PostgreSQL?

I have a table called pins like this: 我有一个像这样的pins表:

id (int) |      pin_codes (jsonb)
--------------------------------
1        |  [4000, 5000, 6000]
2        |  [8500, 8400, 8600]
3        |  [2700, 2300, 2980]

Now, I want the row with pin_code 8600 and with its array index. 现在,我想要使用pin_code 8600及其数组索引的行。 The output must be like this: 输出必须如下:

     pin_codes       |  index
------------------------------
[8500, 8500, 8600]   |   2

If I want the row with pin_code 2700 , the output : 如果我想要pin_code 2700的行,输出:

     pin_codes       |  index
------------------------------
[2700, 2300, 2980]   |   0

What I've tried so far: 到目前为止我尝试过的:

SELECT pin_codes FROM pins WHERE pin_codes @> '[8600]'

It only returns the row with wanted value. 它只返回具有所需值的行。 I don't know how to get the index on the value in the pin_codes array! 我不知道如何获取pin_codes数组中的值的索引!

Any help would be great appreciated. 任何帮助将非常感谢。

PS: PS:

I'm using PostgreSQL 10 我正在使用PostgreSQL 10

Use the function jsonb_array_elements_text() using with ordinality. 使用函数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)

If you were storing the array as a real array not as a json, you could use array_position() to find the (first) index of a given element: 如果您将数组存储为真实数组而不是json,则可以使用array_position()来查找给定元素的(第一个)索引:

select array_position(array['one', 'two', 'three'], 'two') 

returns 2 返回2

With some text mangling you can cast the JSON array into a text array: 通过一些文本修改,您可以将JSON数组转换为文本数组:

select array_position(translate(pin_codes::text,'[]','{}')::text[], '8600')
from the_table;

The also allows you to use the "operator" 还允许你使用“操作员”

select *
from pins
where '8600' = any(translate(pin_codes::text,'[]','{}')::text[])

The contains @> operator expects arrays on both sides of the operator. contains @>运算符需要运算符两侧的数组。 You could use it to search for two pin codes at a time: 您可以使用它一次搜索两个密码:

select *
from pins
where translate(pin_codes::text,'[]','{}')::text[] @> array['8600','8400']

Or use the overlaps operator && to find rows with any of multiple elements: 或者使用重叠运算符&&查找包含多个元素的行:

select *
from pins
where translate(pin_codes::text,'[]','{}')::text[] && array['8600','2700']

would return 会回来的

id | pin_codes         
---+-------------------
 2 | [8500, 8400, 8600]
 3 | [2700, 2300, 2980]

If you do that a lot, it would be more efficient to store the pin_codes as text[] rather then JSON - then you can also index that column to do searches more efficiently. 如果你这么做,那么将pin_codes存储为text[]而不是JSON会更高效 - 那么你也可以索引该列以更有效地进行搜索。

As has been pointed out previously the array_position function is only available in Postgres 9.5 and greater. 如前所述,array_position函数仅在Postgres 9.5及更高版本中可用。

Here is custom function that achieves the same, derived from nathansgreen at github. 是自定义函数,实现相同,源自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$;

So in this specific case, after creating the function the following worked for me. 所以在这种特殊情况下,创建函数后,以下对我有用。

SELECT 
pin_codes,
array_position(pin_codes, 8600) AS index
FROM pins
WHERE array_position(pin_codes, 8600) IS NOT NULL;

Worth bearing in mind that it will only return the index of the first occurrence of 8600, you can use the pos argument to index which ever occurrence that you like. 值得注意的是,它只会返回第一次出现的8600的索引,您可以使用pos参数来索引您喜欢的事件。

In short, normalize your data structure, or don't do this in SQL. 简而言之,规范化您的数据结构,或者不要在SQL中执行此操作。 If you want this index of the sub-data element given your current data structure , then do this in your application code (take result, cast to list/array, get index). 如果您希望给定当前数据结构的子数据元素的索引,那么在您的应用程序代码中执行此操作(获取结果,转换为列表/数组,获取索引)。

Try to unnest the string and assign numbers as follows: 尝试取消字符串并分配数字,如下所示:

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'

If you insist on storing Arrays, I'd defer to klins answer. 如果你坚持存储数组,我会推迟克林斯回答。

As the alternative answer and extension to my comment...don't store SQL data in arrays. 作为我的评论的替代答案和扩展......不要将SQL数据存储在数组中。 'Normalize' your data in advance and SQL will handle it significantly better. 提前“规范化”您的数据,SQL将更好地处理它。 Klin's answer is good, but may suffer for performance as it's outside of what SQL does best. Klin的答案很好,但可能因性能而受到影响,因为它超出了SQL最擅长的范围。

I'd break the Array prior to storing it. 我在存储之前打破了数组。 If the number of pincodes is known, then simply having the table pin_id,pin1,pin2,pin3, pinetc... is functional. 如果已知pincodes的数量,那么简单地将表pin_id,pin1,pin2,pin3,pinetc ......起作用。

If the number of pins is unknown, a first table as pin that stored the pin_id and any info columns related to that pin ID, and then a second table as pin_id, pin_seq,pin_value is also functional (though you may need to pivot this later on to make sense of the data). 如果引脚数未知,则第一个表作为存储pin_id的引脚和与该引脚ID相关的任何信息列,然后第二个表作为pin_id,pin_seq,pin_value也可以使用(尽管您可能需要稍后将其转换了解数据)。 In this case, select pin_seq where pin_value = 260 would work. 在这种情况下,选择pin_seq,其中pin_value = 260将起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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