PostgreSQL-лучший способ вернуть массив пар ключ-значение

Я пытаюсь выбрать несколько полей, одно из которых должно быть массивом с каждым элементом массива, содержащим два значения. Каждый элемент массива должен содержать имя (переменный символ) и идентификатор (числовой). Я знаю, как вернуть массив одиночных значений (используя ARRAY ключевое слово), но я не уверен, как вернуть массив объекта, который сам по себе содержит два значения.

запрос что-то вроде

SELECT
    t.field1,
    t.field2,
    ARRAY(--with each element containing two values i.e. {'TheName', 1 })
FROM MyTable t

Я прочитал это в одну сторону для этого необходимо выбрать значения в тип, а затем создать массив этого типа. Проблема в том, что остальная часть функции уже возвращает тип (что означает, что у меня будут вложенные типы - это нормально? Если да, то как бы вы прочитали эти данные обратно в коде приложения - т. е. с поставщиком данных .Net, таким как NPGSQL?)

любая помощь очень ценится.

2 ответов


Я подозреваю, что, не имея больше знаний о вашем приложении, я не смогу получить вас полностью до нужного вам результата. Но мы можем уйти довольно далеко. Для начала, есть :

# SELECT 'foo', ROW(3, 'Bob');
 ?column? |   row   
----------+---------
 foo      | (3,Bob)
(1 row)

так что прямо там позволяет вам связать целую строку в ячейку. Вы также можете сделать вещи более явными, сделав для него тип:

# CREATE TYPE person(id INTEGER, name VARCHAR);
CREATE TYPE
# SELECT now(), row(3, 'Bob')::person;
              now              |   row   
-------------------------------+---------
 2012-02-03 10:46:13.279512-07 | (3,Bob)
(1 row)

кстати, всякий раз, когда вы делаете таблицу, PostgreSQL делает тип с тем же именем, поэтому, если вы у вас уже есть такая таблица, у вас также есть тип. Например:

# DROP TYPE person;
DROP TYPE

# CREATE TABLE people (id SERIAL, name VARCHAR);
NOTICE:  CREATE TABLE will create implicit sequence "people_id_seq" for serial column "people.id"
CREATE TABLE

# SELECT 'foo', row(3, 'Bob')::people;
 ?column? |   row   
----------+---------
 foo      | (3,Bob)
(1 row)

см. в третьем запросе, который я использовал people как тип.

теперь это, скорее всего, не так поможет, как вы думаете, по двум причинам:

  1. Я не могу найти удобный синтаксис для извлечения данных из вложенной строки.

    возможно, я что-то упускаю, но я просто не вижу много людей, использующих этот синтаксис. Единственный пример, который я вижу в документация-это функция, принимающая значение строки в качестве аргумента и что-то с ним делающая. Я не вижу примера вытягивания строки из ячейки и запроса к ее частям. Похоже, вы можете упаковать данные таким образом, но после этого трудно деконструировать. Вам придется сделать много хранимых процедур.

  2. возможно, драйвер PostgreSQL вашего языка не сможет обрабатывать данные, вложенные в строку.

    Я не могу говорить за NPGSQL, но поскольку это очень специфичная для PostgreSQL функция, вы не найдете поддержки для нее в библиотеках, поддерживающих другие базы данных. Например, Hibernate не сможет обрабатывать извлечение объекта, хранящегося как значение ячейки в строке. Я даже не уверен, что JDBC сможет дать Hibernate информацию с пользой, поэтому проблема может пойти довольно глубоко.

Итак, то, что вы делаете здесь, возможно при условии, что вы можете жить без большого количества точности. Однако я бы не советовал продолжать его, потому что это будет тяжелая битва на всем пути, если я действительно не дезинформирован.


массивы могут содержать только элементы одного типа

в вашем примере отображается text и integer значение (без кавычек 1). Обычно невозможно смешивать типы в массиве. Чтобы получить эти значения в массив, вам нужно создать composite type а затем сформировать массив этого составного типа, как вы уже упоминали сами.

как вариант вы можете использовать типы данных json in Postgres 9.2+,jsonb в Postgres 9.4+ или hstore для пар ключ-значение.


конечно, вы можете бросить integer to text, и работа с двумерным массивом текста. Рассмотрим два варианта синтаксиса для ввода массива в демо ниже и проконсультируйтесь руководство по вводу массива.

существуют ограничения преодолеть. Если вы попытаетесь объединить массив (построить из ключа и значения) в двумерный массив, агрегатная функция array_agg() или ARRAY конструктор ошибка:

ERROR:  could not find array type for data type text[]

есть способы обойти это, хотя.

совокупный пары ключ-значение в 2-мерный массив

PostgreSQL 9.1 с standard_conforming_strings= on:

CREATE TEMP TABLE tbl(
 id     int
,txt    text
,txtarr text[]
);

столбец txtarr просто существует, чтобы продемонстрировать варианты синтаксиса в команде INSERT. Третья строка усеяна метасимволами:

INSERT INTO tbl VALUES
 (1, 'foo', '{{1,foo1},{2,bar1},{3,baz1}}')
,(2, 'bar', ARRAY[['1','foo2'],['2','bar2'],['3','baz2']])
,(3, '}b",a{r''', '{{1,foo3},{2,bar3},{3,baz3}}'); -- txt has meta-characters

SELECT * FROM tbl;

простой случай: агрегат 2 целое число (я использую то же самое дважды) в двумерный массив int:

обновление: лучше с пользовательской агрегатной функции

С полиморфного типа anyarray это работает для всех базовых типов:

CREATE AGGREGATE array_agg_mult (anyarray)  (
    SFUNC     = array_cat
   ,STYPE     = anyarray
   ,INITCOND  = '{}'
);

звоните:

SELECT array_agg_mult(ARRAY[ARRAY[id,id]]) AS x        -- for int
      ,array_agg_mult(ARRAY[ARRAY[id::text,txt]]) AS y -- or text
FROM   tbl;

дополнительный ARRAY[] layer, чтобы сделать его многомерным массивом.

обновление для Postgres 9.5+

Postgres теперь поставляется вариант array_agg() прием ввод массива и вы можете заменить мою пользовательскую функцию сверху следующим:

инструкции:

array_agg(expression)
...
входные массивы, объединенные в массив одного высшее измерение (входные данные должны иметь одинаковую размерность и не могут быть пустым или нулевым)