PostgreSQL中的数组数组

har*_*don 4 sql postgresql plpgsql

我在PostgreSQL的hstore类型上使用%%运算符,它将hstore(有效键值类型)转换为数组,其元素交替{{key,value},{key value}}.

当我想返回这些扁平hstores的数组时,我得到了这个错误:could not find array type for data type text[]由于PostgreSQL缺乏对数组数组的支持.

从好奇的角度来看,有谁知道为什么不支持这些?更重要的是,是否有针对这种情况的解决方案?

目前我将结果连接成一个字符串(逗号分隔)并在应用程序(C#和NPGSQL)端解析它们.但是,这种方法感觉不太对,我希望能够将行读回作为.NET数组的数组或键值数组等.

非常感谢.

Pet*_*uss 17

PostgreSQL支持"阵列数组"

见手册

它是"阵列阵列"的限制形式.正如Pavel(回答)所说,它被命名为"多维数组"但实际上是一个矩阵,因此它必须在每个维度中具有相同数量的元素.

您可以在科学应用程序中将此类结构用于地图多维和异构笛卡尔坐标,但不能存储XML或JSON数据等矢量的任意向量.

注意:众所周知的二维(2D)齐次阵列是数学矩阵.事实上,矩阵的科学应用推动了"PostgreSQL约束多维数组"数据类型,并且数组函数行为具有这类数组.将"3D阵列"视为"3D矩阵",将"4D阵列"视为"4D矩阵",依此类推.

例子:

SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
---------------------
 {{1,2},{3,4},{5,6}}
SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[[5,6]]); -- SAME RESULT

SELECT ARRAY[ARRAY[1,2],ARRAY[5,6]];
---------------
 {{1,2},{5,6}}

SELECT array_cat(ARRAY[ARRAY[1,2]],ARRAY[3]); -- ERROR1
SELECT ARRAY[ARRAY[1,2],ARRAY[4]];  -- ERROR2 
Run Code Online (Sandbox Code Playgroud)

@Daniel_Lyons关于"为什么不支持这些"的注释是关于"非均匀阵列数组"(参见上面的错误情况). ERROR1上面:因为只能连接ERROR2上面相同维度的 数组:特定维度的所有数组必须具有相同的长度,如矩阵.

关于内置函数和运算符的另一个奇怪的事情:PostgreSQL中的"默认行为"是针对单个数组和元素.标准没有过载array_append(),

SELECT array_append(ARRAY[1,2],5); -- now ok, 5 is a element
 {1,2,5}

SELECT array_cat(ARRAY[1,2], ARRAY[5,6]);
----------
 {1,2,5,6}

SELECT array_append(ARRAY[[1,2],[3,4]], ARRAY[5,6]); -- ERROR3 
SELECT array_append(ARRAY[1,2],ARRAY[5,6]); -- ERROR4
Run Code Online (Sandbox Code Playgroud)

ERROR3上面:没有OVERLOAD来追加"数组元素"(甚至9.2 pg版本). ERROR4上面:必须使用array_cat"合并所有在一个数组中".

最后一个array_cat例子的"合并行为" 很奇怪,没有产生数组.使用array_cat(a1, ARRAY[a2])为实现这一结果,

SELECT array_cat(ARRAY[1,2], ARRAY[ARRAY[5,6]]);  -- seems illogical...
---------------
{{1,2},{5,6}}
Run Code Online (Sandbox Code Playgroud)

稀疏矩阵

为避免稀疏矩阵和类似数据结构的问题,请使用以下函数.它填充剩余的元素,然后设置为NULL(或任何常量值).

 CREATE or replace FUNCTION array_fillTo(
    p_array anyarray, p_len integer, p_null anyelement DEFAULT NULL
 ) RETURNS anyarray AS $f$
   SELECT CASE 
       WHEN len=0 THEN array_fill(p_null,array[p_len])
       WHEN len<p_len THEN p_array || array_fill($3,array[$2-len])
       ELSE $1 END
   FROM ( SELECT COALESCE( array_length(p_array,1), 0) ) t(len)
 $f$ LANGUAGE SQL IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)

回到第一个例子,现在我们可以避免错误(参见ERROR1),

SELECT array_cat(ARRAY[ARRAY[1,2]],array_fillTo(ARRAY[3],2));
-- {{1,2},{3,NULL}}
SELECT array_cat(
   ARRAY[ARRAY[1.1::float,2.0]],
   array_fillTo(ARRAY[]::float[],2,0::float)
);
-- {{1.1,2},{0,0}}
SELECT array_fillto(array['Hello'],2,'');
-- {Hello,""}
Run Code Online (Sandbox Code Playgroud)


Dan*_*ons 8

从好奇的角度来看,有谁知道为什么不支持这些?

一个通用的答案是因为数组本质上是反关系的.删除重复值是您获得第一范式的方法.从关系理论的角度来看,重复群体的重复组似乎是非常疯狂的.

通常,关系正确的事情是为重复值提取表.所以,如果你建模这样的东西:

CREATE TABLE users (
  id integer primary key,
  name varchar,
  favorite_colors varchar[],
  ...
);
Run Code Online (Sandbox Code Playgroud)

你应该像以下那样重新定义这种关系:

CREATE TABLE users (
  id integer primary key,
  name varchar,
  ...
);

CREATE TABLE favorite_colors (
  user_id integer references users,
  color varchar
);
Run Code Online (Sandbox Code Playgroud)

甚至:

CREATE TABLE users (
  id integer primary key,
  name varchar,
  ...
);

CREATE TABLE colors (
  color varchar primary key
);

CREATE TABLE favorite_colors (
  user_id integer references users,
  color varchar references colors,
  primary key (user_id, color)
);
Run Code Online (Sandbox Code Playgroud)

Hstore支持许多功能,其中许多功能可以很容易地将其集成到关系世界观中.我认为解决问题的最简单方法是使用该each函数将hstore值转换为关系,然后可以像普通的值一样使用它们.这就是你如何解决在其他数据库中有多个值的问题:查询和使用结果集.

  • 哦!PostgreSQL 支持数组数组(参见我的答案)...是的,在阅读之前我不明白 PostgreSQL 的一些行为,但是*这是关于数组一致性的约束*,如果您想要,例如,将 JSON 映射到统一的多维数组。均匀性约束确保了类似于_正规形式_的东西,但不完全是第一个。 (2认同)

Pav*_*ule 5

PostgreSQL支持多维数组 - 数组在关系数据库中是相对非常特殊的类型,并且它对一般编程语言有点限制.如果需要,可以使用行数组的变通方法:

postgres=# create table fx(a int[]);
CREATE TABLE
postgres=# insert into fx values(array[1,3,4]);
INSERT 0 1
postgres=# insert into fx values(array[6,7]);
INSERT 0 1
postgres=# select array_agg(row(a)) from fx;
            array_agg            
---------------------------------
 {"(\"{1,3,4}\")","(\"{6,7}\")"}
(1 row)
Run Code Online (Sandbox Code Playgroud)