查询Postgres数组列类型

TL;DR我想知道@> {as_champion, whatever}和使用IN ('as_champion', 'whatever') )之间的优缺点(或者它们是否相等)。 详情如下:

我正在使用Rails并使用Postgres的数组列类型,但由于Rails finder方法不能很好地使用它,因此必须使用原始sql作为我的查询。 我发现了一种有效的方法,但想知道首选方法是什么:

Memberships表上的roles列是我的数组列。 它是通过rails添加的,因为:

 add_column :memberships, :roles, :text, array: true 

当我检查表时,它将类型显示为: text[] (不确定这是否真的是Postgres如何表示数组列,或者是否是Rails shenanigans。

要查询它我做的事情如下:

 Membership.where("roles @> ?", '{as_champion, whatever}') 

从精细arrays操作员手册 :

接线员: @>
描述:包含
示例: ARRAY[1,4,3] @> ARRAY[3,1]
结果: t (AKA true)

所以@>将其操作数数组视为集合并检查右侧是否是左侧的子集。

IN有点不同,与子查询一起使用 :

9.22.2。

 expression IN (subquery) 

右侧是带括号的子查询,它必须返回一列。 评估左侧表达式并将其与子查询结果的每一行进行比较。 如果找到任何相等的子查询行,则IN的结果为“true”。 如果找不到相等的行,则结果为“false”(包括子查询不返回任何行的情况)。

或者使用文字列表 :

9.23.1。

 expression IN (value [, ...]) 

右侧是带括号的标量表达式列表。 如果左侧表达式的结果等于任何右侧表达式,则结果为“true”。 这是一个简写符号

 expression = value1 OR expression = value2 OR ... 

所以a IN b或多或少意味着:

a等于列表b中的任何值(可以是生成单个元素行或文字列表的查询)。

当然,你可以这样说:

 array[1] in (select some_array from ...) array[1] in (array[1], array[2,3]) 

但是那些情况下的数组仍然被视为单个值(恰好有一些内部结构)。


如果要检查数组是否包含任何值列表,那么@>不是您想要的。 考虑一下:

 array[1,2] @> array[2,4] 

4不在array[1,2]因此array[2,4]不是array[1,2]的子集。

如果你想检查某人是否同时拥有这两个角色:

 roles @> array['as_champion', 'whatever'] 

是正确的表达式但是如果要检查roles是否是这些值中的任何一个 ,那么您需要重叠运算符( && ):

 roles && array['as_champion', 'whatever'] 

请注意,我正在使用各种数组的“数组构造函数”语法,这是因为使用工具(如ActiveRecord)更方便,它知道在替换占位符时将数组扩展为逗号分隔列表但是不完全理解SQL数组。

鉴于这一切,我们可以说:

 Membership.where('roles @> array[?]', %w[as_champion whatever]) Membership.where('roles @> array[:roles]', :roles => some_ruby_array_of_strings) 

一切都会按预期工作。 你仍然在使用很少的SQL片段(因为ActiveRecord没有完全理解SQL数组或任何表示@>操作符的方法),但至少你不必担心引用问题。 您可以通过AREL手动添加@>支持,但我发现AREL很快就会变成一个难以理解且难以理解的混乱,除了最琐碎的用途。

Interesting Posts