【问题标题】:Rails 4 querying against postgresql column with array data type errorRails 4查询带有数组数据类型错误的postgresql列
【发布时间】:2013-06-28 22:40:39
【问题描述】:

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

而且成功了。

【问题讨论】:

  • 感谢您努力编写详细的问题,包括您的代码、确切的错误消息等。最好也始终包含您的 PostgreSQL 版本 (SELECT version()) 虽然在这种情况下它没有看起来这并不重要。

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


【解决方案1】:

我认为它与 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])

【讨论】:

  • 是的,这行得通,但我应该更具体一些。真的,我正在寻找如何使用 ActiveRecord 进行查询,如下所示:DbOfExercise.where(body_part: params[:body_parts])。我不提前知道用户将选择什么值,因此使用 OR 语句似乎不可行。我在上面编辑了我的问题。
  • 数组重叠肯定是你想要的。 = ANY 对左侧的多值没有意义。
  • +1 表示postgres_ext。解决了从 Rails 进行查询的问题。
【解决方案2】:

@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 列表中每个元素的相等 比较。如果你真的想知道两个数组是否重叠,那不是你想要的。

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

【讨论】:

  • 谢谢,很好的解释。 text[] 和 varchar[] 真的是一回事吗?在对 body_part 列使用 && 运算符时,我遇到了一个错误,必须将 ARRAY['Arms', 'Chest'] 类型转换为 varchar(在我的问题中对此进行了扩展)。
  • @kwyoung11 他们不是完全相同的东西; varchar(n) 带有长度限制。它们可以相互隐式转换,并且在大多数地方是等价的,但是有一些地方不允许 Pg 将它们视为相同。它们是相同的内存和磁盘数据类型,只是长度限制迫使它有时会稍微不同地对待它们。我避免使用varchar,而只是在任何地方使用text。如果我想要一个长度约束,我会添加一个显式的 check 约束。我发现这更清晰、更一致,但我不必编写可移植模式。
  • 明白了,这是有道理的。我认为可能存在性能差异,但根据文档,情况并非如此。
猜你喜欢
  • 2013-06-21
  • 1970-01-01
  • 2011-12-27
  • 2014-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-19
  • 1970-01-01
相关资源
最近更新 更多