ruby-on-rails Ruby on Rails:pluck(* some_hash. values)有什么作用?

wf82jlnq  于 2023-03-04  发布在  Ruby
关注(0)|答案(4)|浏览(178)

我偶然发现了这段代码:

users = User.where([...])

users.group!(:character_id, :age)

user_zip = {
  character_id: :character_id,         (=> has one character)
  age:          :age,
  amount:       Arel.sql('SUM(amount)')
}

user_cleaned = users.pluck(*user_zip.values)

然后,输出是一个包含“转换”(?)用户数据的数组。
这里到底发生了什么?我以前从来没有见过这个。有没有可能用这个做一个单独的记录?

7nbnzgx9

7nbnzgx91#

这段代码中有几件值得注意的事情:

  • 使用*的数组重构
  • pluck
  • Arel.sql
  • 单个记录
    • 结构调整(*)**

拔力定义为
pluck(*column_names)
这意味着它的参数数量是可变的。
如果你用
pluck([:age, :name])
该方法将其视为数组的数组([[:age, :name]]),因此必须"解构"数组firs:
pluck(*[:age, :name])
或者首先用单个值调用它:
pluck(:age, :name)
(如果您以数组形式接收值,就像您的情况一样,这可能是不可能的)
See this Thoughtbot post for more details

    • 拔毛**

pluck做两件事:

  • 它修改生成的SQL语句的select子句
  • 并且它绕过ActiveRecord模型的创建而仅返回数据。

因此以下行返回相同的数据

User.all.map { |user| [user.age, user.name] }
User.all.select(:age, :name).map { |user| [user.age, user.name] }
User.pluck(:age, :name)

第一种是效率最低的:它选择ALL列并创建模型,以便在调用map时丢弃它们
第二种方法效率更高,因为它只选择所需的列,但仍然创建模型。
pluck没有选择 * 所有 * 列和创建模型的开销,因此它通常更快,内存效率更高。
在SQL级别上,它可以归结为以下两个方面的区别
select * from users
以及
select age, name from users
因此,IMHO pluck对于API或导出到CSV等时特别有用。

    • 相对于sql**

Rails希望您将不安全的SQL Package 在Arel.sql中,这是一种安全措施(我个人觉得这很烦人,也很没用),让您知道您有一个潜在的不安全操作,Arel/ActiveRecord无法为您检查。

    • 单个记录**

您不能在单个记录上调用它(并且这也没有多大意义,因为单个记录已经是执行的DB查询的结果,并且那里已经构造了Model)。
如果您想对已从DB加载的单个记录执行类似操作:
用户.属性. values_at('年龄','姓名')
也可以将查询限制为仅返回一条记录的结果
User.where(id: 123).pluck(:age, :name) # only one record since PK
User.where(some conditions).limit(1).pluck(:age, :name)
请注意,pluck的返回值有些不一致:
如果只提取一个属性,则返回一个值数组
["卡尔","迈克"]
但是当你提取多个属性时它会返回一个数组的数组值
x1米20英寸1x

r1zhe5dt

r1zhe5dt2#

让我们从结尾开始分解:
user_zip.values表示“获取Hashuser_zip并返回每个键的值的数组。”例如,下面的哈希:

user_zip = {
  character_id: :character_id,
  age:          :age,
  amount:       Arel.sql('SUM(amount)')
}

会再回来:

[
  :character_id,
  :age,
  Arel.sql('SUM(amount)')
]

再往上一步,user_cleaned = users.pluck(*user_zip.values)使用splat operator将上面的数组解构为参数,这些参数被传递到pluck(),这样您实际上运行的是:

users.pluck(
  :character_id,
  :age,
  Arel.sql('SUM(amount)')
)

至于您提出的将此函数用于单个记录的问题,pluck的用途是从多个记录中检索值,如果您只希望检索单个记录,则不使用pluck(并且它不是一个用于单个记录的方法)但是您可以限制where()查询,使其返回单个记录,例如.limit(1),然后您可以在其上使用pluck,如下所述:

users = User.limit(1).where([...])
nwo49xxi

nwo49xxi3#

这段代码是一种非常复杂的方式:

user_cleaned = users.group(:character_id, :age).pluck(
  :character_id,
  :age,
  Arel.sql('SUM(amount)')
)

组合散列并从中提取值是聪明的,但完全没有意义--它不会使代码更容易理解或维护。
这里到底发生了什么?
pluck选择列列表并立即触发数据库查询。查询的结果是数组的数组,而不是模型示例。
你会得到这样的结果:

[[1, 25, 100], [5, 61, 2000], [5, 16, 50]]

对于非常简单的情况,这是一个方便的低级工具。但它也被广泛地过度使用。
一半的时间实际上一开始就不需要--就像几乎每次有人做pluck(:id)一样。
select_all做同样的事情-返回原始查询值,但是它将结果行作为散列返回,这样处理起来就简单多了。
可以将此用于单个记录吗?
是的--如果你应用一个limit(1)或者where(id: x)的话。但是这有点愚蠢。创建一个模型示例的开销是可以忽略的。

qco9c6ql

qco9c6ql4#

我将从末尾开始。由于pluck是在数据库级别上作为SELECT pluck_columns FROM ...执行的,因此它不能在单个记录上执行,也不能在记录数组上执行。它必须在“ActiveRecord::Relation”上调用。
如果您在开发环境控制台中运行此命令,您将看到它创建了如下查询:

SELECT character_id, age, SUM(amount) FROM users GROUP BY character_id, age;

因此,“转化”后的数据是具有相同character_id和年龄的用户群体持有的金额。
如果您需要解释代码中的ruby部分,如*user_zip.values,请参考@anothermh的另一个答案

相关问题