PostgreSQL - 返回键值对数组的最佳方法

har*_*don 14 sql database postgresql npgsql

我正在尝试选择一些字段,其中一个字段需要是一个数组,数组的每个元素都包含两个值.每个数组项都需要包含名称(字符变化)和ID(数字).我知道如何返回单个值的数组(使用ARRAY关键字),但我不确定如何返回一个对象的数组,该对象本身包含两个值.

查询是这样的

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

我读到一种方法是通过选择一个类型的值,然后创建该类型的数组.问题是,函数的其余部分已经返回一个类型(这意味着我会有嵌套类型 - 是吗?如果是这样,你将如何在应用程序代码中读回这些数据 - 即使用像NPGSQL这样的.Net数据提供程序?)

任何帮助深表感谢.

Erw*_*ter 12

ARRAY只能容纳相同类型的元素

您的示例显示a textinteger值(没有单引号1).通常不可能在数组中混合类型.要将这些值放入数组中,您必须创建一个composite type,然后像您自己提到的那样形成该复合类型的ARRAY.

或者,您可以使用jsonPostgres 9.2 +中的数据类型,Postgres 9.4+中的数据类型jsonbhstore键值对.


当然,您可以转换integertext,并使用二维文本数组.在下面的演示中考虑数组输入的两种语法变体,并参考有关数组输入的手册.

有一个限制需要克服.如果您尝试将ARRAY(从键和值构建)聚合为二维数组,则聚合函数array_agg()ARRAY构造函数错误输出:

ERROR:  could not find array type for data type text[]
Run Code Online (Sandbox Code Playgroud)

不过,有很多方法可以解决它.

将键值对聚合为二维数组

PostgreSQL 9.1 standard_conforming_strings= on:

CREATE TEMP TABLE tbl(
 id     int
,txt    text
,txtarr text[]
);
Run Code Online (Sandbox Code Playgroud)

该列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;
Run Code Online (Sandbox Code Playgroud)

简单的情况:将两个整数(我使用相同的两次)聚合成一个二维的int数组:

更新:使用自定义聚合功能更好

使用多态类型,anyarray它适用于所有基类型:

CREATE AGGREGATE array_agg_mult (anyarray)  (
    SFUNC     = array_cat
   ,STYPE     = anyarray
   ,INITCOND  = '{}'
);
Run Code Online (Sandbox Code Playgroud)

呼叫:

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;
Run Code Online (Sandbox Code Playgroud)

注意附加ARRAY[]层使其成为多维数组.

Postgres 9.5+更新

Postgres现在提供了array_agg()接受数组输入的变体,您可以使用以下方法替换上面的自定义函数:

手册:

array_agg(expression)
...
输入数组连接成一个更高维的数组(输入必须都具有相同的维度,不能为空或NULL)


Dan*_*ons 5

我怀疑如果没有更多的应用程序知识,我将无法让你一路走到你需要的结果.但我们可以走得很远.首先,有ROW功能:

# SELECT 'foo', ROW(3, 'Bob');
 ?column? |   row   
----------+---------
 foo      | (3,Bob)
(1 row)
Run Code Online (Sandbox Code Playgroud)

这样就可以将整行捆绑到一个单元格中.您还可以通过为其创建类型来使事情更明确:

# 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)
Run Code Online (Sandbox Code Playgroud)

顺便说一下,每当你创建一个表时,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)
Run Code Online (Sandbox Code Playgroud)

在第三个查询中看到我people就像一个类型.

现在这不太可能像你想象的那样有两个原因:

  1. 我找不到任何方便的语法来从嵌套行中提取数据.

    我可能会遗漏一些东西,但我没有看到很多人使用这种语法.我在文档中看到的唯一示例是将行值作为参数并使用它执行某些操作的函数.我没有看到一个将行拉出单元格并查询部分内容的示例.看起来你可以用这种方式打包数据,但之后很难解构.你最终必须制作大量的存储过程.

  2. 您的语言的PostgreSQL驱动程序可能无法处理嵌套在一行中的行值数据.

    我不能代表NPGSQL,但由于这是一个非常PostgreSQL特性,你不会在支持其他数据库的库中找到它的支持.例如,Hibernate无法处理获取存储为行中单元格值的对象.我甚至不确定JDBC是否能够有效地为Hibernate提供信息,因此问题可能会非常深入.

所以,你在这里做的事情是可行的,前提是你可以在没有很多细节的情况下生活.我建议不要追求它,因为它会在整个过程中成为一场艰苦的战斗,除非我真的被误导了.