Rails 4使用数组数据类型错误查询postgresql列

时间:2013-06-28 22:40:40

标签: sql ruby-on-rails ruby-on-rails-4 postgresql-9.2

我正在尝试使用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;

并且有效。

2 个答案:

答案 0 :(得分: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 => params [:body_parts])

答案 1 :(得分: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不知道如何将它与字符串进行比较。

我真的不喜欢这种行为;如果你想要匿名行,PostgreSQL要求你明确使用ROW()构造函数,我更喜欢它。我希望此处显示的行为可以支持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_part列的类型为text[](或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列表中每个元素的相等比较。如果你真的想知道两个数组是否重叠,那就不是你想要的了。

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