简体   繁体   English

查询Postgres数组列类型

[英]Query against a Postgres array column type

TL;DR I'm wondering what the pros and cons are (or if they are even equivalent) between @> {as_champion, whatever} and using IN ('as_champion', 'whatever') is. TL;DR我想知道@> {as_champion, whatever}和使用IN ('as_champion', 'whatever')之间的优缺点(或者是否等同)。 Details below: 详情如下:

I'm working with Rails and using Postgres' array column type, but having to use raw sql for my query as the Rails finder methods don't play nicely with it. 我正在使用Rails并使用Postgres的数组列类型,但是由于Rails finder方法不能很好地使用它,因此不得不对查询使用原始sql。 I found a way that works, but wondering what the preferred method is: 我找到了一种可行的方法,但想知道首选的方法是:

The roles column on the Memberships table is my array column. Memberships表上的roles列是我的数组列。 It was added via rails as so: 它是通过rails这样添加的:

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

When I examine the table, it shows the type as: text[] (not sure if that is truly how Postgres represents an array column or if that is Rails shenanigans. 当我检查表时,它显示的类型为: text[] (不确定这是否是Postgres真正表示数组列的方式,还是那是Rails shenanigans。

To query against it I do something like: 要查询它,我要做类似的事情:

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

From the fine Array Operators manual : 从优良的Array Operators手册中

Operator: @> 操作员: @>
Description: contains 说明:包含
Example: ARRAY[1,4,3] @> ARRAY[3,1] 示例: ARRAY[1,4,3] @> ARRAY[3,1]
Result: t (AKA true) 结果: t (又称是)

So @> treats its operand arrays as sets and checks if the right side is a subset of the left side. 因此@>将其操作数数组视为集合,并检查右侧是否是左侧的子集。

IN is a little different and is used with subqueries : IN有点不同,并且与子查询一起使用

9.22.2. 9.22.2。 IN

 expression IN (subquery) 

The right-hand side is a parenthesized subquery, which must return exactly one column. 右侧是带括号的子查询,该查询必须恰好返回一列。 The left-hand expression is evaluated and compared to each row of the subquery result. 评估左侧表达式并将其与子查询结果的每一行进行比较。 The result of IN is "true" if any equal subquery row is found. 如果找到相等的子查询行,则IN的结果为“ true”。 The result is "false" if no equal row is found (including the case where the subquery returns no rows). 如果找不到相等的行,则结果为“ false”(包括子查询不返回任何行的情况)。

or with literal lists : 或带有文字列表

9.23.1. 9.23.1。 IN

 expression IN (value [, ...]) 

The right-hand side is a parenthesized list of scalar expressions. 右侧是带括号的标量表达式列表。 The result is "true" if the left-hand expression's result is equal to any of the right-hand expressions. 如果左侧表达式的结果等于任何右侧表达式,则结果为“ true”。 This is a shorthand notation for 这是的简写

 expression = value1 OR expression = value2 OR ... 

So a IN b more or less means: 因此a IN b或多或少意味着:

Is the value a equal to any of the values in the list b (which can be a query producing single element rows or a literal list). a等于列表b中的任何值(可以是产生单个元素行或文字列表的查询)。

Of course, you can say things like: 当然,您可以这样说:

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

but the arrays in those cases are still treated like single values (that just happen to have some internal structure). 但是在这种情况下,数组仍被视为单个值(恰好具有某些内部结构)。


If you want to check if an array contains any of a list of values then @> isn't what you want. 如果要检查数组是否包含值列表中的任何一个,则@>不是您想要的。 Consider this: 考虑一下:

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

4 isn't in array[1,2] so array[2,4] is not a subset of array[1,2] . 4不在array[1,2]因此array[2,4]不是array[1,2]的子集。

If you want to check if someone has both roles then: 如果要检查某人是否同时具有这两个角色,请:

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

is the right expression but if you want to check if roles is any of those values then you want the overlaps operator ( && ): 是正确的表达式,但是如果要检查roles是否是这些值中的任何一个 ,则需要重叠运算符( && ):

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

Note that I'm using the "array constructor" syntax for the arrays everywhere, that's because it is much more convenient for working with a tool (such as ActiveRecord) that knows to expand an array into a comma delimited list when replacing a placeholder but doesn't fully understand SQL arrays. 请注意,我到处都在对数组使用“数组构造函数”语法,这是因为使用工具(例如ActiveRecord)更方便,该工具在替换占位符时知道将数组扩展为以逗号分隔的列表,但不能完全理解SQL数组。

Given all that, we can say things like: 考虑到所有这些,我们可以说诸如此类:

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

and everything will work as expected. 一切都会按预期进行。 You're still working with little SQL snippets (as ActiveRecord doesn't have a full understanding of SQL arrays or any way of representing the @> operator) but at least you won't have to worry about quoting problems. 您仍在使用少量的SQL代码段(因为ActiveRecord对SQL数组或以任何方式表示@>运算符没有完全的了解),但至少您不必担心引用问题。 You could probably go through AREL to manually add @> support but I find that AREL quickly devolves into an incomprehensible and unreadable mess for all but the most trivial uses. 您可能可以通过AREL手动添加@>支持,但是我发现AREL很快演变为除了最琐碎的用途之外所有其他用途的令人费解和难以理解的混乱。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM