Rails 初学者 - 查询多个表以一次检索大量信息的最佳实践 [英] Rails beginner - Best practice for querying multiple tables to retrieve lots of info at once

查看:30
本文介绍了Rails 初学者 - 查询多个表以一次检索大量信息的最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Rails 初学者,为了学习它,我正在构建一个简单的时间跟踪应用程序.我想用嵌套信息的许多表中的大量信息填充管理员的仪表板.

I'm a Rails beginner and to learn it I'm building a simple time tracking app. I want to populate an administrator's dashboard with a ton of information from many tables with nested information.

查询数据库以请求一家公司查看所有客户、项目、任务、调整和会议记录的仪表板的所有数据的最佳做法是什么?

What would be the best practice for querying the database to request all of the data for one company to view a dashboard of all clients, projects, tasks, adjustments and minutes?

以下是数据的结构:

公司有_许多客户

客户所属公司有_许多项目

Client belongs_to company has_many projects

项目属于客户有_许多任务

Project belongs_to client has_many tasks

任务所属项目有_很多分钟

Task belongs_to project has_many minutes

分钟属于任务

这个数据结构可能真的很糟糕.我不知道.

This data structure might be really bad. I don't know.

数据的示例视图:

动视
-- 网站改版
--- 开发
---- 100 分钟

Activision
-- Website Redesign
--- Development
---- 100 Minutes

我从这个开始,但我很漂亮,但它可能完全倒退(用户属于公司):

I'm starting with this but I'm pretty but it could be totally backwards (Users belong to Companies):

    @clients = Client.find_all_by_company_id(current_user.company_id)
    @clients.each do |client| 

        project = Project.find_all_by_client_id(client.id)
        puts project.name

        project.each do |project|

            task = Task.find_all_by_project_id(project.id)
            puts task.name

        end
    end

我想这个问题也可以问:是否有一本好书或资源可以完整描述 Rails ActiveRecord 最佳实践?

I guess the question can also be asked: Is there a good book or resource that fully describes Rails ActiveRecord best practices?

推荐答案

使用 includes 方法来急切地加载关联.

Use the includes method to eagerly load the associations.

来自指南

Category.includes(:posts => [{:comments => :guest}, :tags]).find(1)

根据你所说的,应该是:

Based on what you said, that should be:

require 'active_record'
require 'logger'

# =====  Config  =====
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
ActiveRecord::Base.logger = Logger.new $stdout
ActiveSupport::LogSubscriber.colorize_logging = false

# =====  Schema  =====
ActiveRecord::Schema.define do
  self.verbose = false

  create_table :clients do |t|
    t.string  :name
    t.integer :company_id
  end

  create_table :companies do |t|
    t.string :name
  end

  create_table :projects do |t|
    t.string  :name
    t.integer :client_id
  end

  create_table :tasks do |t|
    t.string  :name
    t.integer :project_id
  end

  create_table :minutes do |t|
    t.integer :quantity
    t.integer :task_id
  end
end

# =====  Classes  =====
class Company < ActiveRecord::Base
  has_many :clients
end

class Client < ActiveRecord::Base
  belongs_to :company
  has_many   :projects
end

class Project < ActiveRecord::Base
  belongs_to :client
  has_many   :tasks
end

class Task < ActiveRecord::Base
  belongs_to :project
  has_many   :minutes
end

class Minute < ActiveRecord::Base
  belongs_to :task
end

# =====  Data  =====
Company.create! name: 'Activision' do |company|
  company.clients.build name: 'Robert Kotick' do |client|
    client.projects.build name: 'Website Redesign' do |project|
      project.tasks.build name: 'Development' do |task|
        task.minutes.build quantity: 100
      end
    end
  end
end

# =====  Querying and displaying  =====
company = Company.find_by_name 'Activision'
clients = Client.includes(projects: {tasks: :minutes}).where(company_id: company.id)

print "\n----- The query makes four requests, regardless of how much data you have. -----\n\n"
clients.inspect # do this to force loading since AR queries are lazy

print "\n----- some representation of the data (notice no queries while iterating through) -----\n\n"
clients.each do |client|
  puts client.name
  client.projects.each do |project|
    puts "-- #{project.name}"
    project.tasks.each do |task|
      puts "--- #{task.name}"
      task.minutes.each do |minute|
        puts "---- #{minute.quantity}"
      end
    end
  end
end

# =====  Output =====

# >> D, [2012-09-12T00:01:42.755414 #72855] DEBUG -- :    (0.7ms)  select sqlite_version(*)
# >> D, [2012-09-12T00:01:42.755890 #72855] DEBUG -- :    (0.2ms)  CREATE TABLE "clients" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "company_id" integer) 
# >> D, [2012-09-12T00:01:42.756327 #72855] DEBUG -- :    (0.1ms)  CREATE TABLE "companies" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255)) 
# >> D, [2012-09-12T00:01:42.756728 #72855] DEBUG -- :    (0.1ms)  CREATE TABLE "projects" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "client_id" integer) 
# >> D, [2012-09-12T00:01:42.757122 #72855] DEBUG -- :    (0.1ms)  CREATE TABLE "tasks" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "project_id" integer) 
# >> D, [2012-09-12T00:01:42.757531 #72855] DEBUG -- :    (0.1ms)  CREATE TABLE "minutes" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "quantity" integer, "task_id" integer) 
# >> D, [2012-09-12T00:01:42.906877 #72855] DEBUG -- :    (0.0ms)  begin transaction
# >> D, [2012-09-12T00:01:42.909242 #72855] DEBUG -- :   SQL (0.5ms)  INSERT INTO "companies" ("name") VALUES (?)  [["name", "Activision"]]
# >> D, [2012-09-12T00:01:42.934937 #72855] DEBUG -- :   SQL (24.7ms)  INSERT INTO "clients" ("company_id", "name") VALUES (?, ?)  [["company_id", 1], ["name", "Robert Kotick"]]
# >> D, [2012-09-12T00:01:42.936110 #72855] DEBUG -- :   SQL (0.1ms)  INSERT INTO "projects" ("client_id", "name") VALUES (?, ?)  [["client_id", 1], ["name", "Website Redesign"]]
# >> D, [2012-09-12T00:01:42.937001 #72855] DEBUG -- :   SQL (0.1ms)  INSERT INTO "tasks" ("name", "project_id") VALUES (?, ?)  [["name", "Development"], ["project_id", 1]]
# >> D, [2012-09-12T00:01:42.937767 #72855] DEBUG -- :   SQL (0.1ms)  INSERT INTO "minutes" ("quantity", "task_id") VALUES (?, ?)  [["quantity", 100], ["task_id", 1]]
# >> D, [2012-09-12T00:01:42.938005 #72855] DEBUG -- :    (0.0ms)  commit transaction
# >> D, [2012-09-12T00:01:42.939882 #72855] DEBUG -- :   Company Load (0.1ms)  SELECT "companies".* FROM "companies" WHERE "companies"."name" = 'Activision' LIMIT 1
# >> 
# >> ----- The query makes four requests, regardless of how much data you have. -----
# >> 
# >> D, [2012-09-12T00:01:42.940458 #72855] DEBUG -- :   Client Load (0.1ms)  SELECT "clients".* FROM "clients" WHERE "clients"."company_id" = 1
# >> D, [2012-09-12T00:01:42.943272 #72855] DEBUG -- :   Project Load (0.1ms)  SELECT "projects".* FROM "projects" WHERE "projects"."client_id" IN (1)
# >> D, [2012-09-12T00:01:42.943919 #72855] DEBUG -- :   Task Load (0.1ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" IN (1)
# >> D, [2012-09-12T00:01:42.944520 #72855] DEBUG -- :   Minute Load (0.1ms)  SELECT "minutes".* FROM "minutes" WHERE "minutes"."task_id" IN (1)
# >> 
# >> ----- some representation of the data (notice no queries while iterating through) -----
# >> 
# >> Robert Kotick
# >> -- Website Redesign
# >> --- Development
# >> ---- 100

这是一个可怕的德米特法则违反,如果这些事情在任何时候发生变化,无论是在它们的结构还是命名上,我们都必须来修复此代码.我不太确定如何在不引入大量抽象的情况下处理这个问题.

This is a horrible Law of Demeter violation, if any of these things change at any point, whether in their structure or naming, we will have to come fix this code. I'm not really sure how to deal with that without introducing lots of abstractions.

关于一本书,已经有很多了,但老实说,我认为 Rails 世界还没有弄清楚什么是最好的 ActiveRecord 实践(事实上,社区中有很大一部分人认为几乎所有的 ActiveRecord 实践都只是太糟糕了——我大部分时间都在那个营地).

Regarding a book, there have been many, but I honestly don't think the Rails world has figured out yet what constitute best ActiveRecord practices (in fact, there's a large portion of the community that thinks almost all ActiveRecord practices are just terrible -- I'm mostly in that camp).

但是,如果您想要类似上述内容,即使用 #includes 来预先加载关联,那么指南是查找此类信息的好地方.我也非常喜欢这个博客和视频.

But if you want things like the above, which says to use #includes to eager load associations, then the guides are a great place to find out information like that. I also really enjoyed this blog and videos.

这篇关于Rails 初学者 - 查询多个表以一次检索大量信息的最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆