【问题标题】:How to properly write SQL queries in Rails [closed]如何在 Rails 中正确编写 SQL 查询 [关闭]
【发布时间】:2020-05-24 14:10:48
【问题描述】:

我有一个包含两个表的数据库:usersposts,其中一个用户可以有很多帖子。

在某些情况下我想使用纯 SQL 而不是 ActiveRecord,因为纯 SQL 更快。

在我的 UsersController 中有一个方法 index,如下所示:

def index
    @users = User.get_users_and_posts
end

此方法返回有帖子的用户并返回他们的帖子。 get_users_and_posts 方法在我的模型中实现:

class User < ApplicationRecord
...
 private

 def self.get_users_and_posts
   ActiveRecord::Base.connection.execute("
     SELECT
       users.name, users.email, posts.title, posts.body
     FROM
       users
     INNER JOIN
       posts
     ON
       users.id = posts.user_id")
 end

这是在 Rails 中编写 SQL 查询的正确方法吗?它遵循 Rails 方式吗?

【问题讨论】:

  • 因为普通 SQL 更快,比什么都快?
  • "...比什么更快?"确切地。 Rails、Sequel 或手工生成的 SQL 都在 DBM 中全速运行。而且,几年前,ORM 生成的 SQL 通常效率很低,但现在它非常好。再加上 DBM 在执行之前优化查询这一事实,对于不是经验丰富的 DBA 的开发人员来说,使用 ORM 开始有很大的优势。
  • 如果您避免使用 Active Record 进行手动编码,您将牺牲代码的可移植性和便利性。 Active Record 使得为许多不同的 DBM 编写语法正确的 SQL 变得非常容易,允许您在开发和生产中经常使用相同的代码,而只需更改连接字符串。尝试使用手动编码的 SQL。另外,如果将来某个时候,另一个 Rails 开发人员接管了代码的支持,他们不需要知道 SQL,他们只需要知道 Active Record,不管 DB 在什么 DBM 平台下运行。所以“它遵循 Rails 方式吗?”没有。
  • 以上评论是不必要的教条。我们中的很多人都从事过数据库密集型应用程序,是的,事实上,我们有时可以(并且需要)在数据库中对某些事物建模一种非“标准”(按照 AR 标准)方式因此查询它们 使用原始 SQL 以获得 10-100 倍的性能提升。这并不是说我的 SQL 比 AR 生成的更好,而是需要以 AR 无法做到(或无法做到干净)的方式来表示某些东西。 OP 要求以正确的方式执行原始 SQL,评论者应该尊重这一点。
  • 我应该补充一点,我的假设(与其他评论者不同)是这 不是 XY 问题,并且 OP 简化了他们的示例并且知道为什么手写 SQL 会更多在他们的用例中表现出色。

标签: sql ruby-on-rails ruby sqlite


【解决方案1】:

在这种情况下,您不必编写原始 SQL,您可能需要编写原始 SQL,但在这种情况下,您可以使用 ActiveRecord 中提供的更好的解决方案。

避免在 Rails 中进行 N+1 查询,创建具有适当关系的模型。

在用户模型中:

has_many :posts

在 Post 模型中:

belongs_to :user

在您的控制器或存储库中,使用 includes 获取用户的帖子:

User.includes(:posts)

前面几行只做了两个查询:

  1. 获取用户。
  2. 获取所有用户的帖子。

然后它在运行时将帖子映射到他们的用户。

在您之前的示例中,您进行 N+1 个查询,一个查询获取用户,N 个查询获取每个用户的帖子。

有关详细信息,请参阅“10 Tips for Eager Loading to Avoid n+1 Queries in Rails”。

【讨论】:

  • 非常感谢,我会更新答案
  • 这是怎么回事?它必须包括使用范围?
  • 也许您必须重新阅读文档@thiaguerd 或自己尝试。 User.includes(:posts) 可能会返回与 User.all 相同的结果,但它们绝不相同。
  • 非常感谢,我很抱歉这个错误。谢谢你的解释。
  • @magdy 好人继续努力
【解决方案2】:

你基本上已经搞定了!通过为 SQL 查询使用变量使其更易于回收,如下所示:

def self.get_users_and_posts
  sql = ("
     SELECT
       users.name, users.email, posts.title, posts.body
     FROM
       users
     INNER JOIN
       posts
     ON
       users.id = posts.user_id")
  result_array = ActiveRecord::Base.connection.execute(sql)
end

result_array 是您的 SQL 查询以数组形式返回的结果,然后您可以对其进行迭代。

在 Ruby 中编写原始 SQL 查询是否正确?是的。正如The Rails Way中指出的那样:

可以使用 ActiveRecord 的底层数据库连接 直接,有时从自定义脚本中这样做很有用 用于一次性或临时测试。

虽然,Active Record 更像是“Rails 方式”...

ActiveRecord 表达了约定优于配置的理念 这是 Rails 方式的重要组成部分

... 我曾参与过一个特定的数据库繁重的项目,其中直接的数据库连接(如上述)对于速度至关重要。

【讨论】:

  • 你不需要存储ActiveRecord::Base.connection.execute的结果,因为Ruby方法体中最后一个评估语句被返回(默认)。
  • 不知道为什么它被否决了。我有兴趣。欢迎评论。
  • @SebastianPalma 你是 100% 正确的,没有必要(但我没有说是)。但它更清楚,并显示了谁可能不清楚这个事实会发生什么。
  • @SharkLasers 我猜你有一些反对意见,因为很多开发人员似乎相信使用原始 SQL 100% 总是不好的。同时,它只是另一个工具,就像你说的那样,在 DB 繁重的项目中存在有效的用例,其中原始 SQL 的性能提升基本上是强制性的。 (并且可以将操作速度提高整个数量级。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多