ActiveRecord is an Object Relational Mapper (ORM). It can represent business data and logic, however we are going to focus only in its ability to facilitate the creation and use of ruby objects that map business data that is persisted into a relational database.
Throughout this workshop, we are going to tackle 3 topics:
- Performing database operations in a object-oriented fashion
- Representing data models and their associations
- Explaining how to evolve a database schema over time using migrations
ActiveRecord offers a rich interface that permits chaining methods in order to build from simple to complex queries. For instance, methods such as select, from, joins, where
always return ActiveRecord::Relation objects. These objects are only lazy evaluated when you try to access to one of the records returned by the SQL generated.
This library also has some query methods that are not chainable, i.e. they are evaluated immediately and return single values (e.g. an instance of a model, an Integer, an Array of values, etc). The most commond used methods are find, find_by, pluck
, as well as aggregated methods such as avg, count, min, max, etc
.
- where: permits building the WHERE clause according to the arguments passed. When multiple chained where are used these are concatenated using sql
AND
. - and, or, not: permits adding logical operands to the where clause.
- group: performs sql
GROUP BY
on args. More info, visit. - having: performs sql
HAVING
on args. This method is used to perform conditions like sqlWHERE
but permits adding aggregate functions. Remember, this method requires your query to includegroup
. More info, visit. - distinct: removes duplicated records according to the args passed. More info, visit.
These methods when used, modify the select statement of a query from SELECT * ...
to the arguments passed. There are currently 3 different ways:
- select: It returns
ActiveRecord::Relation
therefore it can be chained with other query methods. - pluck: Returns an Array of the attribute values. If the resulting query returns more than one record from the db, it would be an Array of Arrays.
- pick: It behaves like
pluck
but returns only one record using sqlLIMIT 1
underneath.
It is important to understand that neither pluck
nor pick
are chainable.
- preload: loads into memory the relation tables into separate queries. More info, visit.
- eager_load: loads into memory the relation tables using a single SQL query. More info, visit.
- includes: loads into memory the relation tables but it is smarter than
preload
when conditions on related tables are passed. More info, visit.
- joins: performs sql
INNER JOIN
on args. More info, visit. - left_outer_joins: performs sql
LEFT OUTER JOIN
on args. It is behaviour used for eager loading methods such aseager_load
andincludes
.
There are several methods used for mapping into SQL aggregates.
A subquery is a query within a query. It permits increasing the expressive power of SQL. Subqueries can be used in different places inside of statements such as SELECT
, WHERE
, HAVING
or EVEN
. Also in UPDATE
or DELETE
.
There are two types of subqueries:
- simple: where the inner query does not reference columns from the outer query and
- correlated: where the inner query references columns from the outer query. These type of queries tend to be slow since they imply many executions
We can benefit of subqueries through ActiveRecord using from. More info, please visit.
TBD
ActiveRecord brings the possibility to override the type of an existing attribute defined in a
model. For instance the experiment binary_uuid is overriding the
binary field id with the type ORM::UuidType
. This type dedicated for uuid defines how the values
are converted to and from SQL when assigned to the model. Please, refer to the documentation for more details.
TBD
Following topics are to be explored:
- Use explain and to_sql to get a sense of the plan executed and the SQL query generated respectively.
- Combine several SQL statements as one atomic action using Transaction.
- Optimistic/Pesimistic locking through ActiveRecord.
- Combine
ActiveRecord::Relation
using merge. - Advanced ActiveRecord queries using private API Arel.
-
Prepare the environment for test:
make test
. -
Retrieve pricing_settings for domestic, i.e. whose currency is similar to the currency for a company, in a country given. Implementation and Test. Hint:
where
andjoins
can be used. -
Retrieve pricing_settings for exchange, i.e. whose currency differs from the currency for a company, in a country given. Implementation and Test. Hint:
where
andjoins
can be used. -
Retrieve companies having pricing_settings whose payment_method behind is from credit_card type. Implementation and Test. Hint:
where
,joins
anddistinct
. -
Retrieve companies having pricing_settings whose payment_method behind is from credit_card type grouped by sector with totals. Implementation and Test. Hint:
select
,where
,joins
,distinct
,group
,count
. -
Retrieve companies offering many pricing_settings grouped by company and country code. When a company has more than one pricing_setting offered, it is considered many. Implementation and Test. Hint:
group
,having
,pluck
. -
There is an query implementation together with test that retrieves a list of pricing offers for a given company and country code given. Run the following commands:
$ make shell
$ bin/console
$ Queries::CompanyOffersForCountry.call
do you find anything suspicious in terms of queries executed? Could it be improved? Hint:
preload
,includes
oreager_load
. -
Retrieve companies offering payment_methods above average spread of pricing_settings. Implementation and Test. Hint:
select
,joins
,where
,from
anddistinct
. -
There is a need to re-compute pricing_settings_count for each and every company. Implementation and Test. Hint:
.find_in_batches
and#update_column
orcorrelated subquery
and.update_all
.