查询Postgres数组列类型

时间:2017-05-12 17:03:33

标签: ruby-on-rails postgresql ruby-on-rails-4 rails-activerecord array-column

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

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

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

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

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

要查询它,我会执行以下操作:

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

1 个答案:

答案 0 :(得分:4)

来自罚款Array Operators manual

  

接线员:@>
  说明:包含
  示例:ARRAY[1,4,3] @> ARRAY[3,1]
  结果:t(AKA是真的)

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

IN略有不同,与subqueries一起使用:

  

<强> 9.22.2。 IN

expression IN (subquery)
     

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

literal lists

  

<强> 9.23.1。 IN

expression IN (value [, ...])
     

右侧是带括号的标量表达式列表。结果是&#34; true&#34;如果左手表达式的结果等于任何右手表达式。这是

的简写符号
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]
{p> 4不在array[1,2]中,因此array[2,4]不是array[1,2]的子集。

如果您想检查某人是否两个角色,那么:

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

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

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

请注意,我使用&#34;数组构造函数&#34;数组无处不在的语法,因为使用工具(例如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很快就会陷入难以理解和难以理解的混乱中,除了最微不足道的用途之外。