Rails中的查询,第1部分

在本文中,您将学习Active Record查询的基础知识,并学习有关SQL的一些基础知识。 它面向想要开始学习更多有关Ruby on Rails中的数据库查询的初学者。

主题

  • 单一物件
  • 多个物件
  • 条件
  • 定购
  • 限度
  • 分组与拥有

Active Record用于查询数据库。 它可以与SQL,PostgresSQL和SQLite一起使用。 为了从数据库中检索记录,您可以使用几种查找器方法。 关于它们的很酷的事情是,您可以省去编写原始SQL的麻烦。

查找器方法的真正作用是什么? 基本上有三件事:您提供的选项将转换为SQL查询。 然后执行SQL查询并从数据库中检索数据。 此外,对于该结果列表中的每一行,我们都会获得与查询相对应的模型的新实例化Ruby对象。

如果您以前没有玩过SQL,那么我会尽力使事情变得简单,并向您介绍最基本的知识。 遵循SQL示例,尝试理解这些简单的查询。 SQL确实不是火箭科学,只是语法需要一点时间来适应。 希望这会激发您的胃口,以寻找一些有用的教程来填补空白。

让我们看一下您可以使用的几种方法:

  • find
  • first
  • last
  • find_by
  • all
  • find_each
  • find_in_batches
  • where
  • order
  • limit
  • offset
  • group
  • having

所有这些都将返回ActiveRecord::Relation的实例。 什么啊 这是一个在module ActiveRecord命名空间的类,它使我们可以调用多个查询方法并将其链接。 该对象是Rails中使用的查询语法的核心。 让我们检查此类对象的类,然后自己看看:

滑轨
Agent.where(name: 'James Bond').class

# => ActiveRecord::Relation

单一物件

  • find

通过此方法,您可以提供对象的主要ID,并为您检索该单个对象。 如果提供ID数组,则还可以检索多个对象。

滑轨
bond = Agent.find(7)
的SQL
SELECT  "agents".* FROM "agents" WHERE "agents"."id" = ? LIMIT 1  [["id", 7]]

这行SQL指出您要从agents表中选择所有( * )属性,并仅“过滤”具有ID 7的记录。限制使它仅从数据库返回一条记录。

  • firstlast

毫不奇怪,这些将为您提供可以通过其主键标识的第一条记录和最后一条记录。 但是,有趣的部分是您可以提供一个可选数字,该数字将返回该记录数的第一个或最后一个。

滑轨
enemy_agents = SpectreAgent.first(10)

enemy_agents = SpectreAgent.last(10)

在引擎盖下,您正在为所提供的数字提供新的限制,并按升序或降序对其进行排序。

的SQL
SELECT  "spectreagents".* FROM "spectreagents"  ORDER BY "spectreagents"."id" ASC LIMIT 10

SELECT  "spectreagents".* FROM "spectreagents"  ORDER BY "spectreagents"."id" DESC LIMIT 10
  • find_by

该查找器返回与您提供的条件匹配的第一个对象。

滑轨
bond = Agent.find_by(last_name: 'Bond')
的SQL
SELECT  "agents".* FROM "agents" WHERE "agents"."last_name" = ? LIMIT 1  [["last_name", "Bond"]]

多个物件

显然,我们经常需要遍历具有某些议程的对象集合。 手动检索单个对象或选定的几个对象是不错的选择,但通常,我们希望Active Record批量检索对象。

向用户显示各种列表是大多数Rails应用程序的基础。 我们需要的是一个功能强大的工具,它具有方便的API为我们收集这些对象-希望这种方式可以使我们避免大部分时间自己编写涉及的SQL。

  • all
滑轨
mi6_agents = Agents.all
的SQL
SELECT "agents".* FROM "agents"

此方法适用于相对较小的对象集合。 尝试想象在所有Twitter用户的集合上执行此操作。 不,不是一个好主意。 相反,我们想要的是对更大的表大小进行更好地调整的方法。

获取整个表格将无法扩展! 为什么? 因为我们不仅需要一堆对象,而且还需要在该表的每行建立一个对象,并将它们放入内存中的数组。 我希望这听起来不是一个好主意! 那么对此有什么解决方案? 分批! 我们将这些集合分为多个批次,这些批次在内存中更易于处理。 oo!

让我们看一下find_eachfind_in_batches 。 两者相似,但在将对象变成块的方式上却有所不同。 他们接受调整批量大小的选项。 默认值为1,000。

  • find_each
滑轨
NewRecruit.find_each do |recruit|
  recruit.start_hellweek
end
的SQL
SELECT  "newrecruits".* FROM "newrecruits"  ORDER BY "newrecruits"."id" ASC LIMIT 1000

在这种情况下,我们将检索默认的1,000名新员工批次,将其放到区块中,然后逐个发送到地狱周。 由于批次正在分割集合,因此我们也可以通过start告诉它们从哪里start 。 假设我们要一次性处理3,000名新兵,并且希望从4,000名开始。

滑轨
NewRecruit.find_each(start: 4000, batch_size: 3000) do |recruit|
  recruit.start_hellweek
end
的SQL
SELECT  "newrecruits".* FROM "newrecruits" WHERE ("newrecruits"."id" >= 4000)  ORDER BY "newrecruits"."id" ASC LIMIT 3000

重申一下,我们首先检索一批3,000个Ruby对象,然后将它们发送到块中。 start让我们指定要开始获取该批处理的记录的ID。

  • find_in_batches

该对象将其批处理作为数组传递到块,将其传递给另一个更喜欢处理集合的对象。 这里的SQL是一样的。

滑轨
NewRecruit.find_in_batches(start: 2700, batch_size: 1350) do |recruits|
  field_kitchen.prepare_food(recruits)
end

条件

  • where

我们需要先走到where然后再继续。 这使我们可以指定限制查询返回的记录数的条件-一种用于“何处”从数据库中检索记录的过滤器。 如果您使用过SQL WHERE子句,那么您可能会感到宾至如归-与此Ruby包装器一样。

在SQL中,这使我们可以指定要影响的表行,基本上是在满足某种条件的地方。 顺便说一下,这是一个可选的子句。 在下面的原始SQL中,我们仅选择通过WHERE成为孤儿的新兵。

从表中选择特定的行。

的SQL
SELECT * FROM Recruits
WHERE FamilyStatus = 'Orphan';

通过where ,您可以使用字符串,哈希或数组指定条件。 将所有这些放在一起,Active Record可让您过滤以下情况:

滑轨
promising_candidates = Recruit.where("family_status = 'orphan'")
的SQL
SELECT "recruits".* FROM "recruits" WHERE (family_status = 'orphan')

很整洁吧? 我想提一下,这仍然是一个查找操作-我们只指定我们要立即过滤此列表的方式。 从所有新兵的名单中,这将返回筛选出的孤儿候选人名单。 此示例是字符串条件。 远离纯字符串条件,因为它们容易受到SQL注入的攻击,因此不被认为是安全的。

论点安全

在上面的示例中,我们将带条件的orphan变量放入字符串中。 由于这是不安全的,因此被认为是不好的做法。 我们需要对变量进行转义以避免此安全漏洞。 如果这对您来说是个新闻,那么您应该阅读有关SQL注入的信息-您的数据库可能依赖于它。

滑轨
promising_candidates = Recruit.where("family_status = ?", 'orphan'")

? 将由参数列表中的下一个值替换为条件值。 因此,问号基本上是一个占位符。 您还可以使用多个?指定多个条件? 并将它们链接在一起。 在现实生活中,我们将使用以下参数散列:

promising_candidates = Recruit.where("family_status = ?", params[:recruits])

如果您有大量可变条件,则应使用键/值占位符条件。

滑轨
promising_candidates = Recruit.where(
                        "family_status = :preferred_status 
                         AND iq >= :required_iq
                         AND charming = :lady_killer",
                         { preferred_status: 'orphan', 
                           required_iq: 140,
                           lady_killer: true
                           }
                         )
的SQL
SELECT "recruits".* FROM "recruits" WHERE (family_status = 'orphan' AND iq >= 140 AND lady_killer = true)

当然,上面的示例很愚蠢,但是它清楚地显示了占位符表示法的好处。 通常,哈希符号绝对是更易读的符号。

滑轨
promising_candidates = Recruit.where(family_status: 'orphan')

promising_candidates = Recruit.where('charming': true)

如您所见,您可以随意使用符号或字符串。 让我们通过NOT通过范围和负条件关闭本节。

滑轨
promising_candidates = Recruit.where(birthday: ('1994-01-01'..'2000-01-01'))

两个点,您可以建立所需的任何范围。

promising_candidates = Recruit.where.not(character: 'coward')

您可以将not塞在where以过滤掉所有胆小鬼,并只获得不具有该特定不需要属性的结果。 在引擎盖下, !=否定WHERE“过滤器”。

的SQL
SELECT "recruits".* FROM "recruits" WHERE ("recruits"."character" != ?)  [["character", "coward"]]

定购

  • order

为了不让您对此感到无聊,让我们快一点。

candidates = Recruit.order(:date_of_birth)
candidates = Recruit.order(:date_of_birth, :desc)

应用:asc:desc对其进行相应排序。 基本上就是这样,让我们​​继续吧!

限度

  • limit

您可以将返回的记录数减少到特定数目。 如前所述,大多数情况下,您不需要返回所有记录。 下面的示例将为您提供数据库中的前五名新兵,即前五个ID。

滑轨
five_candidates = Recruit.limit(5)
的SQL
SELECT  "recruits".* FROM "recruits" LIMIT 5
  • offset

如果您曾经想过分页如何在引擎盖下工作,那么limitoffset结合使用)就可以完成艰苦的工作。 limit可以独立存在,但offset取决于前者。

设置偏移量对于分页最有用,它使您可以跳过数据库中所需的行数。 候选人列表的第二页可以像这样查找:

滑轨
Recruit.limit(20).offset(20)

SQL看起来像这样:

SELECT  "recruits".* FROM "recruits" LIMIT 20 OFFSET 20

同样,我们从Recruit数据库模型中选择所有列,将返回的记录限制为Class Recruit的20个Ruby对象,并跳过前20个。

分组与拥有

假设我们想要一个按智商分组的新兵列表。 在SQL中,这可能看起来像这样。

SELECT "recruits".* FROM "recruits" GROUP BY "recruits"."iq"

这将为您提供一个列表,您可以在其中查看哪些可能的应聘者的智商为120,然后是另一组的智商为140,依此类推-无论他们的智商如何,有多少智商都落在特定数字以下。 因此,当两名新兵的智商为130时,他们将被分组在一起。

可能患有幽闭恐惧症,对身高恐惧或在医学上不适合潜水的候选人可能会列出另一个列表。 Active Record查询将看起来像这样:

  • group
滑轨
Candidate.group(:iq)

当我们计算候选人的数量时,我们会得到一个非常有用的哈希。

滑轨
Candidate.group(:iq).count

# => { 130=>7, 134=>4, 135=>3, 138=>2, 140=>1, 141=>1 }

我们去了—我们有七名可能的新人,智商为130,而只有一名新人的智商为141。产生的SQL看起来像这样:

的SQL
SELECT COUNT(*) AS count_all, iq AS iq FROM "candidates" GROUP BY "candidates"."iq"

重要的是GROUP BY部分。 如您所见,我们使用候选人表获取其ID。 从这个简单的示例中还可以看到,Active Record版本的读取和写入更加方便。 想象一下,在更多奢侈的示例上手动进行此操作。 当然,有时您必须这样做,但是很显然,我们一直很高兴可以避免这种痛苦。

  • having

我们可以通过使用更加指定此组HAVING的一个过滤器的-sort group 。 从这个意义上说, havingGROUP的一种WHERE子句。 换句话说, having依赖于使用group

滑轨
Recruit.having('iq > ?', 134).group(:iq)
的SQL
SELECT "recruits".* FROM "recruits" GROUP BY "recruits"."iq" HAVING iq > '134'

现在,我们将候选人分类为智商最低为135的人员列表。让我们对他们进行计数以获得一些统计信息:

滑轨
Recruit.having('iq > ?', 134).group(:iq).count

# => { 135=>3, 138=>2, 140=>1, 141=>1 }
的SQL
SELECT COUNT(*) AS count_all, iq AS iq FROM "recruits" GROUP BY "recruits"."iq" HAVING iq > '134'

我们还可以将它们混合和匹配,例如,查看哪些智商高于140的候选人是否陷入了恋爱关系。

滑轨
Recruit.having('iq > ?', 140).group(:family_status)
的SQL
SELECT "recruits".* FROM "recruits" GROUP BY "recruits"."family_status" HAVING iq > '140'

现在计算这些组太容易了:

滑轨
Recruit.having('iq > ?', 140).group(:family_status).count

# => { "married"=>2, "single"=>1 }
的SQL
SELECT COUNT(*) AS count_all, family_status AS family_status FROM "recruits" GROUP BY "recruits"."family_status" HAVING iq > '140'

最后的想法

我希望这是对Active Record必须提供的有用信息,以使您的查询工作尽可能地可读和方便。 总体而言,我想说这是一个出色的包装程序,可以使您在大多数情况下不必手动编写SQL。

在下一篇文章中,我们将研究更多参与的发现者,并扩展到目前为止所学的知识。

翻译自: https://code.tutsplus.com/articles/queries-in-rails-part-1--cms-26409

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值