【问题标题】:Compare an array to elements of a multi-dimensional array将数组与多维数组的元素进行比较
【发布时间】:2018-06-28 21:21:34
【问题描述】:

我在 PostgreSQL 9.6 数据库中有一个表:

CREATE TABLE new_table (
 column_name varchar(255)[],
 name        varchar(40)
);

INSERT INTO new_table VALUES
 ('{one, two}'  , 'first_user'),
 ('{other, two}', 'second_user'),
 ('{one, more}' , 'third_user');

我有一个数组数组(二维数组),并希望找到表中column_name 匹配任何包含的一维数组的所有行。我追求的是这样的:

select * 
from new_table as s
where s.column_name = any('{{"one", "two"}, {"one", "more"}, {"two", "five"}}')

但这给了我错误:

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

我想要的结果:

column_name |    name
------------+--------------
{one,two}   | first_user
{one,more}  | third_user

有人知道吗?

【问题讨论】:

  • 如果你想比较(即查询数据),这意味着你不应该首先使用数组。您应该创建一个一对多表来保存您想要为每个用户提供的不同值
  • 为什么不公开你的 Postgres 版本?应该不言而喻...
  • @PanagiotisKanavos 你是什么意思我不应该在表格或数组数组中使用数组?目前我遍历数组并将数组与'{"one", "two"}' = s.column_name 进行比较,我只是好奇我可以通过一次调用而不是多次调用数据库来做到这一点。
  • @ErwinBrandstetter 哦忘了,现在已经编辑帖子了。

标签: sql arrays postgresql multidimensional-array


【解决方案1】:

错误信息

为了记录,你的表面错误:

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

.. 会随着明确的演员表而消失。喜欢:

...
WHERE column_name = any('{{"one", "two"}, {"one", "more"}, {"two", "five"}}'::varchar[])

但这不会解决根本问题:

ERROR:  operator does not exist: character varying[] = character varying

说明

Postgres 对多维数组的支持可能会令人困惑。事实上,varchar(255)[] 在内部解析为与varchar(255)[][] 完全相同的数据类型。

每个数组值的元素类型是varchar(不是varchar[])。 ANY 构造有效地忽略了数组维度。 The manual:

数组比较逐个元素比较数组内容

解决方案

要实现您想要的,您需要将 2D 数组取消嵌套到 1D 数组。为每个数据库创建此函数一次

CREATE OR REPLACE FUNCTION unnest_2d_1d(ANYARRAY, OUT a ANYARRAY)
  RETURNS SETOF ANYARRAY AS
$func$
BEGIN
   FOREACH a SLICE 1 IN ARRAY $1 LOOP
      RETURN NEXT;
   END LOOP;
END
$func$  LANGUAGE plpgsql IMMUTABLE STRICT;

进一步阅读:

现在,所有这些查询都有效:

SELECT t.*
FROM   unnest_2d_1d('{{"one", "two"}, {"one", "more"}, {"two", "five"}}'::varchar[]) a(column_name)
JOIN   new_table t USING (column_name);

或者:

SELECT t.*
FROM   new_table t
WHERE  column_name = ANY ((SELECT unnest_2d_1d('{{"one", "two"}, {"one", "more"}, {"two", "five"}}'::varchar[])));

或者:

...
WHERE  column_name IN ((SELECT unnest_2d_1d('{{"one", "two"}, {"one", "more"}, {"two", "five"}}'::varchar[])));

dbfiddle here

相关:

旁白

这是数组列上的 btree 索引可能有用的罕见情况之一:

CREATE INDEX ON new_table(column_name);

在 Postgres 中 varchar(255) 没有什么特别之处。我大多只使用text。见:

【讨论】:

    【解决方案2】:

    使用数组运算符与运算符一起工作:

    t=# select *
    from new_table as s
    where s.column_name <@ '{{"one", "two"}, {"one", "more"}, {"two", "five"}}';
     column_name |    name
    -------------+------------
     {one,two}   | first_user
     {one,more}  | third_user
    (2 rows)
    

    我假设https://www.postgresql.org/docs/current/static/functions-comparisons.html#idm46428706346880 可能有一些注释,表达式 不是数组,您不能将其用于此类比较

    更新

    为了能够“将数组取消嵌套一维”并使用incomarison 的结果集,请使用Pavel Stěhule,建议使用function

    t=# select * from new_table 
    where column_name in (
      select reduce_dim('{{one, two}, {two, more}, {one, five}}'::character varying[])
    );
     column_name |    name
    -------------+------------
     {one,two}   | first_user
    (1 row)
    

    【讨论】:

    • 是的,这几乎可以工作,但我需要一个方法来比较两个数组是否相等,如果我使用 '{{"one", "two"}, {"two", "more"}, {"one", "five"}}' 会给出相同的结果,在我的情况下我只需要它给出一个结果,因为{"one", "more"} 不在其中。
    • @Elmseld 我包含了切片数组 fn(),但已经准备好了 Erwins 的答案 :)
    猜你喜欢
    • 1970-01-01
    • 2015-06-03
    • 1970-01-01
    • 1970-01-01
    • 2013-09-25
    • 2021-06-24
    • 2015-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多