# Query Builder
- Retrieving a Single Object - find() - The find method retrieve the object corresponding to the specified primary key that matches any supplied options - customer = Customer.find(10) #<Customer id: 10, first_name: "Ryan">1
 2- take() - The take method retrieves a record without any implicit ordering. - customer = Customer.take #<Customer id: 1, first_name: "Lifo"> customers = Customer.take(2) [#<Customer id: 1, first_name: "Lifo">, #<Customer id: 220, first_name: "Sara">]1
 2
 3
 4
 5- first() - The first method finds the first record ordered by primary key (default). - customer = Customer.first #<Customer id: 1, first_name: "Lifo"> customers = Customer.first(3) [#<Customer id: 1, first_name: "Lifo">, #<Customer id: 2, first_name: "Fifo">, #<Customer id: 3, first_name: "Filo">]1
 2
 3
 4
 5- last() - The last method finds the last record ordered by primary key (default). - customer = Customer.last #<Customer id: 221, first_name: "Russel"> customers = Customer.last(3) [#<Customer id: 219, first_name: "James">, #<Customer id: 220, first_name: "Sara">, #<Customer id: 221, first_name: "Russel">] customer = Customer.order(:first_name).last #<Customer id: 220, first_name: "Sara">1
 2
 3
 4
 5
 6
 7
 8- find_by() - The find_by method finds the first record matching some conditions. - Customer.find_by first_name: 'Lifo' #<Customer id: 1, first_name: "Lifo">1
 2
 3
- Retrieving Multiple Objects in Batches - find_each() - The find_each method retrieves records in batches and then yields each one to the block. - Customer.find_each do |customer| NewsMailer.weekly(customer).deliver_now end Customer.where(weekly_subscriber: true).find_each do |customer| NewsMailer.weekly(customer).deliver_now end #batch size option allows you to specify the number of records to be retrieved in each batch Customer.find_each(batch_size: 5000) do |customer| NewsMailer.weekly(customer).deliver_now end #start option allows you to configure the first ID of the sequence whenever the lowest ID is not the one you need. Customer.find_each(start: 2000) do |customer| NewsMailer.weekly(customer).deliver_now end #finish allows you to configure the last ID of the sequence whenever the highest ID is not the one you need. Customer.find_each(start: 2000, finish: 10000) do |customer| NewsMailer.weekly(customer).deliver_now end1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24- find_in_batches() - The find_in_batches method is similar to - find_each, since both retrieve batches of records. The difference is that- find_in_batchesyields batches to the block as an array of models, instead of individually.- # Give add_customers an array of 1000 customers at a time. Customer.find_in_batches do |customers| export.add_customers(customers) end # Give add_customers an array of 1000 recently active customers at a time. Customer.recently_active.find_in_batches do |customers| export.add_customers(customers) end #batch_size Customer.find_in_batches(batch_size: 2500) do |customers| export.add_customers(customers) end #start Customer.find_in_batches(batch_size: 2500, start: 5000) do |customers| export.add_customers(customers) end #finish Customer.find_in_batches(finish: 7000) do |customers| export.add_customers(customers) end1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
- Conditions - The - [where](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/QueryMethods.html#method-i-where)method allows you to specify conditions to limit the records returned, representing the- WHERE-part of the SQL statement. Conditions can either be specified as a string, array, or hash.- #Pure String Conditions Book.where("title = 'Introduction to Algorithms'") #Array Conditions Book.where("title = ?", params[:title]) Book.where("title = ? AND out_of_print = ?", params[:title], false) Book.where("title LIKE ?", Book.sanitize_sql_like(params[:title]) + "%") #Hash Conditions Book.where(out_of_print: true) Book.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight) Customer.where(orders_count: [1,3,5]) #NOT Condition Customer.where.not(orders_count: [1,3,5]) #OR Condition Customer.where(last_name: 'Smith').or(Customer.where(orders_count: [1,3,5])) #AND Condition Customer.where(last_name: 'Smith').where(orders_count: [1,3,5]))1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
- Ordering - To retrieve records from the database in a specific order, you can use the - [order](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/QueryMethods.html#method-i-order)method.- Book.order(:created_at) # OR Book.order("created_at") Book.order(created_at: :desc) # OR Book.order(created_at: :asc) # OR Book.order("created_at DESC") # OR Book.order("created_at ASC") Book.order(title: :asc, created_at: :desc) # OR Book.order(:title, created_at: :desc) # OR Book.order("title ASC, created_at DESC") # OR Book.order("title ASC", "created_at DESC") Book.order("title ASC").order("created_at DESC")1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
- Select Statement - You may not always want to select all columns from a database table. Using the - selectmethod, you can specify a custom "select" clause for the query:- Book.select(:isbn, :out_of_print) # OR Book.select("isbn, out_of_print")1
 2
 3
 4- If you would like to only grab a single record per unique value in a certain field, you can use - [distinct](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/QueryMethods.html#method-i-distinct):- Customer.select(:last_name).distinct1
- Limit and Offset - You can use - limitto specify the number of records to be retrieved, and use- offsetto specify the number of records to skip before starting to return the records. For example- Customer.limit(5)1- Adding - offsetto that- Customer.limit(5).offset(30)1
- Group - To apply a - GROUP BYclause to the SQL fired by the finder, you can use the- [group](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/QueryMethods.html#method-i-group)method.- For example, if you want to find a collection of the dates on which orders were created: - Order.select("created_at").group("created_at")1- To get the total of grouped items on a single query, call - [count](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Calculations.html#method-i-count)after the- group.- irb> Order.group(:status).count => {"being_packed"=>7, "shipped"=>12}1
 2
- Having - SQL uses the - HAVINGclause to specify conditions on the- GROUP BYfields. You can add the- HAVINGclause to the SQL fired by the- Model.findby adding the- [having](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/QueryMethods.html#method-i-having)method to the find.- For example: - Order.select("created_at, sum(total) as total_price"). group("created_at").having("sum(total) > ?", 200)1
 2
- Joining Table - Active Record provides two finder methods for specifying - JOINclauses on the resulting SQL:- joinsand- left_outer_joins. While- joinsshould be used for- INNER JOINor custom queries,- left_outer_joinsis used for queries using- LEFT OUTER JOIN- #Using String Author.joins("INNER JOIN books ON books.author_id = authors.id AND books.out_of_print = FALSE") #Using Array/Hash of Named Associations Book.joins(:reviews) #Using Multiple Association Book.joins(:author, :reviews) #Joining Nested Associations (Single Level) Book.joins(reviews: :customer) #Joining Nested Associations (Multiple Level) Author.joins(books: [{ reviews: { customer: :orders } }, :supplier] ) #Specifying Conditions on the Joined Tables time_range = (Time.now.midnight - 1.day)..Time.now.midnight Customer.joins(:orders).where('orders.created_at' => time_range).distinct #or time_range = (Time.now.midnight - 1.day)..Time.now.midnight Customer.joins(:orders).where(orders: { created_at: time_range }).distinct #left joins Customer.left_outer_joins(:reviews).distinct.select('customers.*, COUNT(reviews.*) AS reviews_count').group('customers.id')1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27- . 
- Eager Loading Relations - preload - Preload loads the association data in a separate query. `` - User.preload(:posts).to_a # => SELECT "users".* FROM "users" SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1)1
 2
 3
 4
 5- This is how includes loads data in the default case. - Since preload always generates two sql we can't use posts table in where condition. Following query will result in an error. - User.preload(:posts).where("posts.desc='ruby is awesome'") # => SQLite3::SQLException: no such column: posts.desc: SELECT "users".* FROM "users" WHERE (posts.desc='ruby is awesome')1
 2
 3
 4
 5- With preload where clauses can be applied. - User.preload(:posts).where("users.name='Neeraj'") # => SELECT "users".* FROM "users" WHERE (users.name='Neeraj') SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (3)1
 2
 3
 4
 5- includes - Includes loads the association data in a separate query just like preload. - However it is smarter than preload. Above we saw that preload failed for query User.preload(:posts).where("posts.desc='ruby is awesome'"). Let's try same with includes. - User.includes(:posts).where('posts.desc = "ruby is awesome"').to_a # => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3 FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE (posts.desc = "ruby is awesome")1
 2
 3
 4
 5
 6
 7
 8- As you can see includes switches from using two separate queries to creating a single LEFT OUTER JOIN to get the data. And it also applied the supplied condition. - So includes changes from two queries to a single query in some cases. By default for a simple case it will use two queries. Let's say that for some reason you want to force a simple includescase to use a single query instead of two. Use references to achieve that. - eager_load - eager loading loads all association in a single query using LEFT OUTER JOIN . - User.eager_load(:posts).to_a # => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3 FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"1
 2
 3
 4
 5
 6- This is exactly what includes does when it is forced to make a single query when where or order clause is using an attribute from posts table. 
- Scopes - Scoping allows you to specify commonly-used queries which can be referenced as method calls on the association objects or models. - To define a simple scope, we use the - [scope](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Scoping/Named/ClassMethods.html#method-i-scope)method inside the class, passing the query that we'd like to run when this scope is called:- class Book < ApplicationRecord scope :out_of_print, -> { where(out_of_print: true) } #chainble scope scope :out_of_print_and_expensive, -> { out_of_print.where("price > 500") } #passing arguments scope :costs_more_than, ->(amount) { where("price > ?", amount) } #book.costs_more_than(500) #conditions scope :created_before, ->(time) { where("created_at < ?", time) if time.present? } #default scope default_scope { where(out_of_print: false) } end1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
- Dynamic Filters - For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called - first_nameon your- Customermodel for example, you get the instance method- find_by_first_namefor free from Active Record. If you also have a- lockedfield on the- Customermodel, you also get- find_by_lockedmethod.- You can specify an exclamation point ( - !) on the end of the dynamic finders to get them to raise an- ActiveRecord::RecordNotFounderror if they do not return any records, like- Customer.find_by_first_name!("Ryan")- If you want to find both by - first_nameand- orders_count, you can chain these finders together by simply typing "- and" between the fields. For example,- Customer.find_by_first_name_and_orders_count("Ryan", 5)
- Enums - An enum lets you define an Array of values for an attribute and refer to them by name. The actual value stored in the database is an integer that has been mapped to one of the values. - Declaring an enum will: - Create scopes that can be used to find all objects that have or do not have one of the enum values
- Create an instance method that can be used to determine if an object has a particular value for the enum
- Create an instance method that can be used to change the enum value of an object
 - for all possible values of an enum. - For example, given this - [enum](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Enum.html#method-i-enum)declaration:- class Order < ApplicationRecord enum :status, [:shipped, :being_packaged, :complete, :cancelled] end1
 2
 3- These scopes (opens new window) are created automatically and can be used to find all objects with or without a particular value for - status:- irb> Order.shipped => #<ActiveRecord::Relation> # all orders with status == :shipped irb> Order.not_shipped => #<ActiveRecord::Relation> # all orders with status != :shipped1
 2
 3
 4- These instance methods are created automatically and query whether the model has that value for the - statusenum:- irb> order = Order.shipped.first irb> order.shipped? => true irb> order.complete? => false1
 2
 3
 4
 5- These instance methods are created automatically and will first update the value of - statusto the named value and then query whether or not the status has been successfully set to the value:- irb> order = Order.first irb> order.shipped! UPDATE "orders" SET "status" = ?, "updated_at" = ? WHERE "orders"."id" = ? [["status", 0], ["updated_at", "2019-01-24 07:13:08.524320"], ["id", 1]] => true1
 2
 3
 4
- Methods Chaining - #Retrieving filtered data from multiple tables Customer .select('customers.id, customers.last_name, reviews.body') .joins(:reviews) .where('reviews.created_at > ?', 1.week.ago) #Retrieving specific data from multiple tables Book .select('books.id, books.title, authors.first_name') .joins(:author) .find_by(title: 'Abstraction and Specification in Program Development')1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
- Find or build New Objects - It's common that you need to find a record or create it if it doesn't exist. You can do that with the - find_or_create_byand- find_or_create_by!methods.- find_or_create_by - The - [find_or_create_by](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Relation.html#method-i-find_or_create_by)method checks whether a record with the specified attributes exists. If it doesn't, then- createis called. Let's see an example.- Customer.find_or_create_by(first_name: 'Andy')1- find_or_create_by! - You can also use - [find_or_create_by!](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Relation.html#method-i-find_or_create_by-21)to raise an exception if the new record is invalid.- find_or_initialize_by - The - [find_or_initialize_by](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Relation.html#method-i-find_or_initialize_by)method will work just like- find_or_create_bybut it will call- newinstead of- create.
- Find by SQL - find_by_sql - Customer.find_by_sql("SELECT * FROM customers INNER JOIN orders ON customers.id = orders.customer_id ORDER BY customers.created_at desc")1- select_all - select_allwill retrieve objects from the database using custom SQL just like- find_by_sqlbut will not instantiate them. This method will return an instance of- ActiveRecord::Resultclass and calling- to_aon this object would return you an array of hashes where each hash indicates a record.- irb> Customer.connection.select_all("SELECT first_name, created_at FROM customers WHERE id = '1'").to_a => [{"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"}, {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}]1
 2
 3- pluck - [pluck](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Calculations.html#method-i-pluck)can be used to query single or multiple columns from the underlying table of a model. It accepts a list of column names as an argument and returns an array of values of the specified columns with the corresponding data type.- irb> Book.where(out_of_print: true).pluck(:id) SELECT id FROM books WHERE out_of_print = true => [1, 2, 3] irb> Order.distinct.pluck(:status) SELECT DISTINCT status FROM orders => ["shipped", "being_packed", "cancelled"] irb> Customer.pluck(:id, :first_name) SELECT customers.id, customers.first_name FROM customers => [[1, "David"], [2, "Fran"], [3, "Jose"]]1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11- ids - [ids](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Calculations.html#method-i-ids)can be used to pluck all the IDs for the relation using the table's primary key.- irb> Customer.ids SELECT id FROM customers1
 2
- Existence of Objects - If you simply want to check for the existence of the object there's a method called - [exists?](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F). This method will query the database using the same query as- find, but instead of returning an object or collection of objects it will return either- trueor- false.- Customer.exists?(1) Customer.exists?(id: [1,2,3]) # or Customer.exists?(first_name: ['Jane', 'Sergei']) Customer.where(first_name: 'Ryan').exists?1
 2
 3
 4
 5
 6
 7
- Calculations - #count Customer.count #average Order.average("subtotal") #minimum Order.minimum("subtotal") #maximum Order.maximum("subtotal") #sum Order.sum("subtotal")1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14