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
как тип.
теперь это, скорее всего, не так поможет, как вы думаете, по двум причинам:
-
Я не могу найти удобный синтаксис для извлечения данных из вложенной строки.
возможно, я что-то упускаю, но я просто не вижу много людей, использующих этот синтаксис. Единственный пример, который я вижу в документация-это функция, принимающая значение строки в качестве аргумента и что-то с ним делающая. Я не вижу примера вытягивания строки из ячейки и запроса к ее частям. Похоже, вы можете упаковать данные таким образом, но после этого трудно деконструировать. Вам придется сделать много хранимых процедур.
-
возможно, драйвер 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)
...
входные массивы, объединенные в массив одного высшее измерение (входные данные должны иметь одинаковую размерность и не могут быть пустым или нулевым)