我正在尝试使用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;
并且有效。
答案 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
列表中每个元素的相等比较。如果你真的想知道两个数组是否重叠,那就不是你想要的了。
这就是为什么你要使用数组重叠运算符&&
。