5

我正在尝试在 Rails 4 中查询具有 postgresql 数组数据类型的列的表。

这是表架构:

create_table "db_of_exercises", force: true do |t|
    t.text     "preparation"
    t.text     "execution"
    t.string   "category"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "name"
    t.string   "body_part",      default: [], array: true
    t.hstore   "muscle_groups"
    t.string   "equipment_type", default: [], array: true
  end

以下查询有效:

SELECT * FROM db_of_exercises WHERE ('Arms') = ANY (body_part);

但是,此查询不会:

SELECT * FROM db_of_exercises WHERE ('Arms', 'Chest') = ANY (body_part);

它抛出这个错误:

ERROR:  operator does not exist: record = character varying

这对我也不起作用:

SELECT * FROM "db_of_exercises" WHERE "body_part" IN ('Arms', 'Chest');

这会引发此错误:

ERROR:  array value must start with "{" or dimension information

那么,如何在 ActiveRecord 中查询具有数组数据类型的列?

我现在拥有的是:

@exercises = DbOfExercise.where(body_part: params[:body_parts])

我希望能够查询具有多个关联的 body_part 的记录,这就是使用数组数据类型的全部意义所在,所以如果有人能启发我如何做到这一点,那就太棒了。我在文档中的任何地方都没有看到它。

后代的最终解决方案:

使用重叠运算符 (&&):

SELECT * FROM db_of_exercises WHERE ARRAY['Arms', 'Chest'] && body_part;

我收到了这个错误:

ERROR:  operator does not exist: text[] && character varying[]

所以我将 ARRAY['Arms', 'Chest'] 类型转换为 varchar:

 SELECT * FROM db_of_exercises WHERE ARRAY['Arms', 'Chest']::varchar[] && body_part;

那行得通。

4

2 回答 2

6

我不认为它与rails有关。

如果您执行以下操作会怎样?

SELECT * FROM db_of_exercises WHERE 'Arms' = ANY (body_part) OR 'Chest' = ANY (body_part)

我知道 rails 4 支持 Postgresql ARRAY 数据类型,但我不确定 ActiveRecord 是否创建了用于查询数据类型的新方法。也许您可以使用Array Overlap我的意思是&&运算符,然后执行以下操作:

WHERE ARRAY['Arms', 'Chest'] && body_part

或者看看这个宝石:https ://github.com/dockyard/postgres_ext/blob/master/docs/querying.md

然后执行如下查询:

DBOfExercise.where.overlap(:body_part => 参数[:body_parts])

于 2013-06-28T22:50:29.597 回答
6

@Aguardientico 绝对正确,您想要的是数组重叠运算符&&。我正在跟进一些更多的解释,但希望你接受这个答案,而不是这个。

匿名行(记录)

该构造('item1', 'item2', ...)是一个行构造函数,除非它出现在IN (...)列表中。它创建一个匿名行,PostgreSQL 称之为“记录”。错误:

ERROR:  operator does not exist: record = character varying

是因为('Arms', 'Chest')被解释为好像是ROW('Arms', 'Chest'),这会产生一个record值:

craig=> SELECT ('Arms', 'Chest'), ROW('Arms', 'Chest'), pg_typeof(('Arms', 'Chest'));
     row      |     row      | pg_typeof 
--------------+--------------+-----------
 (Arms,Chest) | (Arms,Chest) | record
(1 row)

并且 PostgreSQL 不知道它应该如何将它与字符串进行比较。

我不太喜欢这种行为;ROW()如果 PostgreSQL 要求您在需要匿名行时显式使用构造函数,我会更喜欢它。我希望此处显示的行为能够支持构造函数没有多大意义SET (col1,col2,col3) = (val1,val2,val3)的其他类似操作。ROW(...)

但是同一个项目的工作原理是一样的吗?

单个('Arms')案例起作用的原因是,除非有逗号,否则它只是一个带括号的值,其中括号是多余的并且可以被忽略:

craig=> SELECT ('Arms'), ROW('Arms'), pg_typeof(('Arms')), pg_typeof(ROW('Arms'));
 ?column? |  row   | pg_typeof | pg_typeof 
----------+--------+-----------+-----------
 Arms     | (Arms) | unknown   | record
(1 row)

不要被类型吓到unknown。它只是意味着它是一个尚未应用类型的字符串文字:

craig=> SELECT pg_typeof('blah');
 pg_typeof 
-----------
 unknown
(1 row)

将数组与标量进行比较

这个:

SELECT * FROM "db_of_exercises" WHERE "body_part" IN ('Arms', 'Chest');

失败:

ERROR:  array value must start with "{" or dimension information

因为隐式转换。列的类型body_parttext[](或varchar[]; 在 PostgreSQL 中相同)。您正在将它与IN子句中的值进行比较,这些值是未知类型的文字。数组唯一有效的相等运算符是=另一个相同类型的数组,因此 PostgreSQL 认为IN子句中的值也必须是数组,text[]并尝试将它们解析为数组。

由于它们没有写成数组字面量,比如{"FirstValue","SecondValue"},所以这个解析失败了。观察:

craig=> SELECT 'Arms'::text[];
ERROR:  array value must start with "{" or dimension information
LINE 1: SELECT 'Arms'::text[];
               ^

看?

一旦你看到这IN实际上只是= ANY. 这是对列表中每个元素的相等比较。IN如果你真的想知道两个数组是否重叠,那不是你想要的。

这就是为什么要使用数组重叠运算符的原因&&

于 2013-06-29T09:25:12.103 回答