diff --git a/assets/images/lessons/joins/inner_join.png b/assets/images/lessons/joins/inner_join.png
new file mode 100644
index 00000000..1300cd4d
Binary files /dev/null and b/assets/images/lessons/joins/inner_join.png differ
diff --git a/assets/images/lessons/joins/left_join.png b/assets/images/lessons/joins/left_join.png
new file mode 100644
index 00000000..8af5d110
Binary files /dev/null and b/assets/images/lessons/joins/left_join.png differ
diff --git a/assets/images/lessons/joins/right_join.png b/assets/images/lessons/joins/right_join.png
new file mode 100644
index 00000000..92858e44
Binary files /dev/null and b/assets/images/lessons/joins/right_join.png differ
diff --git a/module1/projects/index.md b/module1/projects/index.md
index 3b2ecf5d..7093dae6 100644
--- a/module1/projects/index.md
+++ b/module1/projects/index.md
@@ -12,7 +12,7 @@ The project specs will be linked below as each project is assigned.
- Week 1-2 (Solo): [Flash Cards](./flashcards/)
-- Week 2-3 (Solo): TBD
+- Week 2-3 (Solo): [DMV](./dmv/)
- Week 3-4 (Paired): TBD
@@ -20,4 +20,4 @@ The project specs will be linked below as each project is assigned.
## Additional Projects and Resources
-- [Event Manager:](./event_manager.markdown) Supplement to File IO lesson
\ No newline at end of file
+- [Event Manager:](./event_manager.markdown) Supplement to File IO lesson
diff --git a/module2/lessons/class_vs_instance_methods.md b/module2/lessons/class_vs_instance_methods.md
new file mode 100644
index 00000000..53d944a3
--- /dev/null
+++ b/module2/lessons/class_vs_instance_methods.md
@@ -0,0 +1,71 @@
+---
+title: Class vs Instance Methods
+length: 90
+layout: page
+---
+
+## Learning Goals
+
+- Identify use cases for class methods and instance methods in the context of a Rails app
+- Identify available AR methods depending on current context of `self`
+- Differentiate when to use a class method or an instance method
+
+## Set Up
+
+This lesson plan starts with the `class-vs-instance-setup` branch of our good old Set List Tutorial Repo. Get it [here](https://github.com/turingschool-examples/set-list-7/tree/class-vs-instance-setup). Then, follow the normal setup tasks:
+
+- Run `bundle install`
+- Run `rails db:{drop,create,migrate,seed}`
+
+## Exploration
+
+Open up `artist.rb` and `artist_spec.rb` side-by-side in your code editor. You'll notice a `pry` in some of the methods defined in the Artist class. You can run the spec with `bundle exec rspec spec/models/artist_spec.rb` to hit the first pry. You can enter `exit` into pry to continue on to the next pry. Use the code, test, and each of the `prys` to answer the following questions with your group in a break out room:
+
+For each of the methods defined in the Artist class:
+
+- Is the method defined as a class or instance method?
+- What is the value of `self` inside the method?
+- How do you call the method? In other words, what can you call the method on?
+- How can you update the method body to use `self`?
+
+**Discussion Questions**
+
+- What is a class method?
+- What is an instance method?
+- What is self in each context?
+- What does an instance of a model represent in our DB?
+- What does the class of a model represent in our DB?
+
+## Takeaways
+
+Review these after your discussion
+ * In a Rails app, a class method allows you to perform actions on or query data from an entire database table rather than one specific row (or instance) of the data. `self` refers to the entire class or table.
+ * In an instance method, you can perform actions or retrieve information about one instance of the class, which represents one row of data in a database table. `self` in these methods refers to the single model object.
+
+
+## Practice Problems
+
+Using model tests and the corresponding models only, write methods that will:
+
+- Return all songs sorted by title alphabetically
+- Return all of an artist's songs sorted by title alphabetically
+- Return the `x` shortest songs, where `x` is an argument for the method
+- Return the `x` shortest songs for an artist, where `x` is an argument for the method
+
+**Spicy**
+
+- Return a song's artist's name
+- Return the number of songs for an artist that have at least 1 play and a length greater than 0
+- Return a list of songs that have a title that contains the word "love"
+- Return the 3 songs that have the most plays, a length greater than `x` where `x` can be any integer value, and were updated within the last three days
+
+Answers to these practice problems can be found on the `class-vs-instance-solutions` branch [here](https://github.com/turingschool-examples/set-list-7/tree/class-vs-instance-solutions).
+
+## Checks for Understanding
+Answer the following questions either in your notebook or by taking [this review quiz](https://forms.gle/BG6JfUSAhSioYero6).
+
+- How do you know whether a task requires a class or an instance method?
+- What are some common error messages we might see if we confuse a class method with an instance method and vice versa?
+- How can we list the methods available for self in pry?
+- What does an instance of a model represent in our DB?
+- What does the class of a model represent in our DB?
diff --git a/module2/lessons/index.md b/module2/lessons/index.md
index a5c5f694..5674cfb4 100644
--- a/module2/lessons/index.md
+++ b/module2/lessons/index.md
@@ -19,14 +19,17 @@ title: Module 2 - Lessons
## Databases
* [Intro to Databases and ORMs](./databases_and_orms)
-
-## Active Record
* [SQL and Active Record](./sql_and_active_record)
+* [Joins](./joins)
+
## HTML and CSS
* [HTML: Fundamentals](./html_fundamentals)
+## Active Record
+* [Class vs Instance Methods](./class_vs_instance_methods.md)
+
## Additional Resources
* [Chrome Dev Tools](./chrome_dev_tools)
diff --git a/module2/lessons/intro_to_class_methods.md b/module2/lessons/intro_to_class_methods.md
new file mode 100644
index 00000000..7f732a28
--- /dev/null
+++ b/module2/lessons/intro_to_class_methods.md
@@ -0,0 +1,155 @@
+---
+title: Intro to Class Methods (in Rails)
+length: 45
+layout: page
+---
+
+## Learning Goals
+
+- Review Class Methods in Ruby
+- Explore examples of Class Methods using Active Record
+
+### Warm Up
+
+1. Using your own words, write down a definition of 'class methods' in Ruby. How do they work?
+2. The code example below defines a Ruby class with one instance method and one class method. Come up with one more instance method and one more class method on your own, and describe how each of the methods would be used.
+
+```ruby
+class Inventory
+
+ def initialize(name)
+ @name = name
+ @equipment = []
+ end
+
+ def store(equipment)
+ @equipment << equipment
+ "This inventory now contains #{equipment}."
+ end
+
+ def self.store
+ "A person's inventory can store up to 10 pieces of equipment."
+ end
+end
+```
+
+## Set Up
+
+This lesson uses the [Intro to Ruby on Rails repo](https://github.com/turingschool-examples/intro_to_ror_demo/tree/main) from your intermission work. Clone it now if you haven't already, and check out a new `class_methods` branch.
+
+We'll need more data to work with for this lesson. Open the `db/seeds.rb` file and add the following lines:
+
+```ruby
+Subject.create!(title: 'Trigonometry', description: 'Angles and calculating them', difficulty: 4)
+Subject.create!(title: 'Music History', description: 'Beyond the Beatles', difficulty: 1)
+Subject.create!(title: 'Orientation', description: 'Learning about remote learning', difficulty: 1)
+Subject.create!(title: 'Revisions 201', description: 'Drafting was the easy part', difficulty: 4)
+```
+
+Then run the following commands:
+
+```bash
+bundle
+rails db:{drop,create,migrate,seed}
+```
+
+## Exploration
+
+Let's say we need to be able to return the easiest subjects from our database. "Easy" subjects have a difficulty of 3 or less, and this list should be ordered from easiest to most difficult.
+
+This query is straightforward in Active Record. Open your rails console and try this query:
+
+```ruby
+Subject.where("difficulty <= ?", 3).order(:difficulty)
+```
+
+This should return 4 objects that meet the criteria described above. Great! How do you think this works?
+
+### Using class methods in Rails models
+
+We know that in Ruby, class methods are called on the class name itself (rather than on an instance). For example:
+
+```ruby
+my_inventory = Inventory.new("Marcille Donato")
+my_inventory.store("Ambrosia") # instance method called on an instance of the Inventory class
+Inventory.store # class method called on the Inventory class
+```
+
+You might have noticed that we called the Active Record query above on the `Subject` model-- that is, on the class name itself and not an instance. In order to look at all instances of `Subject` in the database and compare them to the criteria we provided, AR **has** to use class methods.
+
+Add this method to the Subject class:
+
+```ruby
+def self.easy_subjects
+ binding.pry
+end
+```
+
+Now, go into `rails c` again and call `Subject.easy_subjects`. What does `self` return in your pry? (You may have to run `Subject.connection` first). Keep in mind that calling `self` inside of a method is very different from prepending `self.` to a method definition.
+
+### What is going on with 'self' in instance and class methods?
+
+The scope within Ruby methods work in such a way that `self` is __implied__. This means that anything we call within this method is called on whatever the `self` object is in that moment!
+
+Therefore, in this pry session, if we wanted to return all rows from the `subjects` table, we don't even need to use the class name `Subject`; it's already implied. Try running these in your pry session:
+
+* `all`
+* `first`
+* `where(title: "Literature")`
+
+To illustrate how `self` can behave differently, let's add an instance method and pry into it:
+
+```ruby
+def format_summary
+ binding.pry
+end
+```
+
+```ruby
+# in rails console:
+subject = Subject.first
+subject.format_summary
+```
+
+In this pry session, `self` returns an `Subject` object, a single instance. Again, `self` is implied, and we don't need to write that in our method in order to call other methods on the object. Try running the following commands in your pry session:
+
+* `id`
+* `title`
+* `created_at`
+
+This means that we can call on a model instance's attributes directly from within an instance method.
+
+```ruby
+def format_summary
+ "#{title} - #{description}"
+end
+```
+
+### Putting it all together
+
+Class methods are extremely valuable in Rails applications. One very common way to utilize them is to make SQL and AR queries reusable and DRY, encapsulating this data logic in a Model rather than writing the query every time it's needed in something like a Controller.
+
+Back to our challenge: return the easiest subjects from our database ordered by ascending difficulty. Put the query we used earlier into a class method, so that it can be called on `Subject`.
+
+```ruby
+class Subject < ApplicationRecord
+
+ def self.easy_subjects
+ where("difficulty <= ?", 3).order(:difficulty)
+ end
+end
+```
+
+Then try it out in `rails c`:
+
+```ruby
+Subject.easy_subjects
+```
+
+## Checks for Understanding
+
+Answer the following questions in your notebook or gist:
+
+- What is the difference between using `self` as part of a method definition, and using `self` from within a method scope?
+- What does the class of a model represent in our DB?
+- What does an instance of a model represent in our DB?
\ No newline at end of file
diff --git a/module2/lessons/joins.md b/module2/lessons/joins.md
new file mode 100644
index 00000000..9afaf5c6
--- /dev/null
+++ b/module2/lessons/joins.md
@@ -0,0 +1,242 @@
+---
+layout: page
+title: Joins in SQL and Active Record
+---
+
+## Learning Goals
+
+- Understand and visualize a SQL join query
+- Implement an ActiveRecord query using `.joins`
+
+
+## Homework & Warm Up
+
+Before this class, try working through the directions in the README file of the [joins-homework](https://github.com/turingschool-examples/set-list-7/tree/joins-homework) branch in Set List Tutorial.
+
+The 2nd part of the `joins-homework` exercises is to try some Join queries on your own, in the `spec/models/playlist_spec.rb` file. Reference this lesson for help writing AR Joins queries.
+
+## Set Up
+
+For this lesson's code-along, you can start work from [this branch](https://github.com/turingschool-examples/set-list-7/tree/generic-start) of the Set List Tutorial.
+```bash
+bundle install
+rails db:{drop,create,migrate,seed}
+```
+
+---
+
+## Joining Tables
+
+### The SQL Join Query
+
+So far, we have looked at SQL and ActiveRecord queries that deal only with one table, or we have asked our database for information related to a single resource. But you will sometimes need to run queries based on information from more than one table. When we come up against this problem, we rely on `JOIN` queries to accomplish this goal.
+
+At the highest level, a `JOIN` pulls information from multiple tables into one temporary table. Let's use our SetList app to see how this works.
+
+In your terminal, run the following command to open your set_list_development database `rails dbconsole`, and let's take a look at our `songs` and `artists` tables:
+
+```bash
+set_list_7_development=# SELECT * FROM songs;
+ id | title | length | play_count | created_at | updated_at | artist_id
+----+-------------------------------+--------+------------+----------------------------+----------------------------+-----------
+ 1 | Raspberry Beret | 345 | 34 | 2023-02-23 16:22:59.81817 | 2023-02-23 16:22:59.81817 | 1
+ 2 | Purple Rain | 524 | 19 | 2023-02-23 16:22:59.820061 | 2023-02-23 16:22:59.820061 | 1
+ 3 | Legend Has It | 2301 | 2300000 | 2023-02-23 16:22:59.821595 | 2023-02-23 16:22:59.821595 | 2
+ 4 | Talk to Me | 2301 | 2300000 | 2023-02-23 16:22:59.822961 | 2023-02-23 16:22:59.822961 | 2
+ 5 | 26 | 940 | 150000 | 2023-02-23 16:22:59.82436 | 2023-02-23 16:22:59.82436 | 3
+ 6 | Vagabond | 240 | 120000 | 2023-02-23 16:22:59.825712 | 2023-02-23 16:22:59.825712 | 3
+ 7 | Aint No Bread In The Breadbox | 540 | 12000 | 2023-02-23 16:22:59.827072 | 2023-02-23 16:22:59.827072 | 4
+ 8 | The Harder They Come | 240 | 120000 | 2023-02-23 16:22:59.828354 | 2023-02-23 16:22:59.828354 | 4
+ 9 | bury a friend | 340 | 1200000 | 2023-02-23 16:22:59.829678 | 2023-02-23 16:22:59.829678 | 5
+ 10 | bad guy | 240 | 100000 | 2023-02-23 16:22:59.830952 | 2023-02-23 16:22:59.830952 | 5
+ 11 | Someone Great | 500 | 1000000 | 2023-02-23 16:22:59.832334 | 2023-02-23 16:22:59.832334 | 6
+ 12 | I Can Change | 640 | 100000 | 2023-02-23 16:22:59.833637 | 2023-02-23 16:22:59.833637 | 6
+(12 rows)
+```
+
+```bash
+set_list_7_development=# SELECT * FROM artists;
+ id | name | created_at | updated_at
+----+-------------------+----------------------------+----------------------------
+ 1 | Prince | 2023-02-23 16:28:12.228288 | 2023-02-23 16:28:12.228288
+ 2 | Run The Jewels | 2023-02-23 16:28:12.229754 | 2023-02-23 16:28:12.229754
+ 3 | Caamp | 2023-02-23 16:28:12.230785 | 2023-02-23 16:28:12.230785
+ 4 | Jerry Garcia Band | 2023-02-23 16:28:12.231814 | 2023-02-23 16:28:12.231814
+ 5 | Billie Eilish | 2023-02-23 16:28:12.232855 | 2023-02-23 16:28:12.232855
+ 6 | LCD Soundsystem | 2023-02-23 16:28:12.233853 | 2023-02-23 16:28:12.233853
+ 7 | Taylor Swift | 2023-02-23 16:28:12.234963 | 2023-02-23 16:28:12.234963
+(7 rows)
+```
+
+Above, we see our songs and artists table - what happens if we `JOIN` these tables together?
+
+```bash
+set_list_7_development=# SELECT artists.*, songs.* FROM songs JOIN artists ON artists.id = songs.artist_id;
+ id | name | created_at | updated_at | id | title | length | play_count | created_at | updated_at | artist_id
+----+-------------------+----------------------------+----------------------------+----+-------------------------------+--------+------------+----------------------------+----------------------------+-----------
+ 1 | Prince | 2023-02-23 16:22:59.802034 | 2023-02-23 16:22:59.802034 | 1 | Raspberry Beret | 345 | 34 | 2023-02-23 16:22:59.81817 | 2023-02-23 16:22:59.81817 | 1
+ 1 | Prince | 2023-02-23 16:22:59.802034 | 2023-02-23 16:22:59.802034 | 2 | Purple Rain | 524 | 19 | 2023-02-23 16:22:59.820061 | 2023-02-23 16:22:59.820061 | 1
+ 2 | Run The Jewels | 2023-02-23 16:22:59.803489 | 2023-02-23 16:22:59.803489 | 3 | Legend Has It | 2301 | 2300000 | 2023-02-23 16:22:59.821595 | 2023-02-23 16:22:59.821595 | 2
+ 2 | Run The Jewels | 2023-02-23 16:22:59.803489 | 2023-02-23 16:22:59.803489 | 4 | Talk to Me | 2301 | 2300000 | 2023-02-23 16:22:59.822961 | 2023-02-23 16:22:59.822961 | 2
+ 3 | Caamp | 2023-02-23 16:22:59.804563 | 2023-02-23 16:22:59.804563 | 5 | 26 | 940 | 150000 | 2023-02-23 16:22:59.82436 | 2023-02-23 16:22:59.82436 | 3
+ 3 | Caamp | 2023-02-23 16:22:59.804563 | 2023-02-23 16:22:59.804563 | 6 | Vagabond | 240 | 120000 | 2023-02-23 16:22:59.825712 | 2023-02-23 16:22:59.825712 | 3
+ 4 | Jerry Garcia Band | 2023-02-23 16:22:59.80559 | 2023-02-23 16:22:59.80559 | 7 | Aint No Bread In The Breadbox | 540 | 12000 | 2023-02-23 16:22:59.827072 | 2023-02-23 16:22:59.827072 | 4
+ 4 | Jerry Garcia Band | 2023-02-23 16:22:59.80559 | 2023-02-23 16:22:59.80559 | 8 | The Harder They Come | 240 | 120000 | 2023-02-23 16:22:59.828354 | 2023-02-23 16:22:59.828354 | 4
+ 5 | Billie Eilish | 2023-02-23 16:22:59.806621 | 2023-02-23 16:22:59.806621 | 9 | bury a friend | 340 | 1200000 | 2023-02-23 16:22:59.829678 | 2023-02-23 16:22:59.829678 | 5
+ 5 | Billie Eilish | 2023-02-23 16:22:59.806621 | 2023-02-23 16:22:59.806621 | 10 | bad guy | 240 | 100000 | 2023-02-23 16:22:59.830952 | 2023-02-23 16:22:59.830952 | 5
+ 6 | LCD Soundsystem | 2023-02-23 16:22:59.807657 | 2023-02-23 16:22:59.807657 | 11 | Someone Great | 500 | 1000000 | 2023-02-23 16:22:59.832334 | 2023-02-23 16:22:59.832334 | 6
+ 6 | LCD Soundsystem | 2023-02-23 16:22:59.807657 | 2023-02-23 16:22:59.807657 | 12 | I Can Change | 640 | 100000 | 2023-02-23 16:22:59.833637 | 2023-02-23 16:22:59.833637 | 6
+(12 rows)
+```
+
+Write in your notebook, what did this query do? How might you describe the return value of this query?
+
+When we run this `JOIN`, we are *joining* the songs and artists tables together to form a return value that is a table that includes all the information from *both* tables. For each artist, we see a row for each song that they have, with the information from both the artists table and the songs table.
+
+When creating a `JOIN` query, there are three parts essential to the query:
+
+1. `SELECT` - this is what indicates which columns will be included in the resulting table
+2. `ON` - this tells the join *how* to join this two tables together, or what is the relationship between the two tables (most often, primary key = foreign key)
+3. `JOIN` - the command to join to tables together!
+
+Looking at our joined table, what information could seem to be missing? WHERE IS `TAYLOR SWIFT`?
+
+Write in your notebook, why are we not seeing ***all*** of our artists on this joined table?
+
+## Types of Join Queries
+
+When we create `JOIN` queries, there are a handful of different join types that we can declare that will affect the resulting table. Today, we are going to cover 3 of those join types: **Left Join**, **Inner Join**, and **Right Join.**
+
+### Inner Join
+
+The default `JOIN` type in SQL is an **Inner JOIN**. An inner join will grab only the information from the two tables where the information matches the `ON` condition - in our example above, it will grab only information for artists who have songs, and their song information. This relationship is often visualized like this:
+
+![Inner Join](../../assets/images/lessons/joins/inner_join.png)
+
+### Left Join
+
+The next most common `JOIN` type is a **Left Join**. A left join will get all the records from one table, regardless of if they have corresponding rows in the joined table. If we run a left join in our setlist app, it could look like this:
+
+```bash
+set_list_7_development=# SELECT artists.id, artists.name, songs.id, songs.title FROM artists LEFT JOIN songs ON songs.artist_id = artists.id;
+ id | name | id | title
+----+-------------------+----+-------------------------------
+ 1 | Prince | 1 | Raspberry Beret
+ 1 | Prince | 2 | Purple Rain
+ 2 | Run The Jewels | 3 | Legend Has It
+ 2 | Run The Jewels | 4 | Talk to Me
+ 3 | Caamp | 5 | 26
+ 3 | Caamp | 6 | Vagabond
+ 4 | Jerry Garcia Band | 7 | Aint No Bread In The Breadbox
+ 4 | Jerry Garcia Band | 8 | The Harder They Come
+ 5 | Billie Eilish | 9 | bury a friend
+ 5 | Billie Eilish | 10 | bad guy
+ 6 | LCD Soundsystem | 11 | Someone Great
+ 6 | LCD Soundsystem | 12 | I Can Change
+ 7 | Taylor Swift | |
+(13 rows)
+```
+
+Now, we see `Taylor Swift` even though that artist has no songs. We visualize this relationship like so:
+
+![Left Join](../../assets/images/lessons/joins/left_join.png)
+
+### Right Join
+
+The last of these join types is a **Right Join** which will get only records from one table if they match with records from the joined table and will get all records from the joined table regardless of if they have a corresponding record from the starting table. We can visualize the join.
+
+![Right Join](../../assets/images/lessons/joins/right_join.png)
+
+## Joining in ActiveRecord
+
+So what does all this look like in ActiveRecord? Open a new tab in your terminal and open your console with `rails c`.
+
+In ActiveRecord, similar to how we can create a SQL `WHERE` with `.where`, we can use `.joins` to create a SQL `JOIN` query!
+
+```bash
+irb(main):001:0> Artist.joins(:songs)
+ Artist Load (1.4ms) SELECT "artists".* FROM "artists" INNER JOIN "songs" ON "songs"."artist_id" = "artists"."id"
+=>
+[#,
+ #,
+ #,
+ #,
+ #,
+ #,
+ #,
+ #,
+ #,
+ #,
+ #,
+ #]
+```
+
+Write in your notebook, why are we not seeing any song information in this `ActiveRecord::Relation`?
+
+Take a look at the SQL query that is generated with the ActiveRecord method call.
+
+```bash
+irb(main):001:0> Artist.joins(:songs)
+ Artist Load (1.4ms) SELECT "artists".* FROM "artists" INNER JOIN "songs" ON "songs"."artist_id" = "artists"."id"
+```
+
+Now, we are SELECTing from artists *and* songs, but has our return value changed? Unfortunately, no. Because we are starting our ActiveRecord query from our Artist model, ActiveRecord will try to create Artist objects from the resulting data; so, we don't see the song information, but it is actually there! We can access it on each of the resulting 'artist' objects.
+
+```bash
+irb(main):002:0> first_record = Artist.select('artists.*, songs.*').joins(:songs).first
+ Artist Load (0.6ms) SELECT artists.*, songs.* FROM "artists" INNER JOIN "songs" ON "songs"."artist_id" = "artists"."id" ORDER BY "artists"."id" ASC LIMIT $1 [["LIMIT", 1]]
+=> #
+
+irb(main):003:0> first_record.title
+=> "Raspberry Beret"
+
+irb(main):004:0> first_record.length
+=> 345
+
+irb(main):005:0> first_record.play_count
+=> 34
+```
+
+## Practice
+
+Let's see this in action by imagining that we might want to be able to get a list of artists who have songs longer than '400'. Work with a partner to get this information using both SQL and ActiveRecord. The solutions are below.
+
+### SQL
+
+```bash
+set_list_7_development=# SELECT artists.name FROM artists JOIN songs ON artists.id = songs.artist_id WHERE songs.length > 400;
+ name
+-------------------
+ Prince
+ Run The Jewels
+ Run The Jewels
+ Caamp
+ Jerry Garcia Band
+ LCD Soundsystem
+ LCD Soundsystem
+(7 rows)
+```
+
+### ActiveRecord
+
+```bash
+irb(main):001:0> Artist.joins(:songs).where('songs.length > ?', 400)
+ Artist Load (1.1ms) SELECT "artists".* FROM "artists" INNER JOIN "songs" ON "songs"."artist_id" = "artists"."id" WHERE (songs.length > 400)
+=>
+[#,
+ #,
+ #,
+ #,
+ #,
+ #,
+ #]
+```
+
+## Checks for Understanding
+
+1. What are the three types of joins covered today? And, what do they return?
+2. What is the SQL query to get a list of Artists who have songs that have been played more than 20 times?
+3. What is the ActiveRecord query to get a list of Artists who have songs that have been played more than 20 times?
+
+## Further Reading
+For an exploration of how to join multiple tables together, and advanced joining techniques, review the lesson [here](./joins_2).
\ No newline at end of file
diff --git a/module2/lessons/joins_2.md b/module2/lessons/joins_2.md
new file mode 100644
index 00000000..38475a78
--- /dev/null
+++ b/module2/lessons/joins_2.md
@@ -0,0 +1,163 @@
+---
+layout: page
+title: Multiple Joins
+---
+
+## Prerequisites
+For success in this lesson, be sure you have reviewed the [Joins](./joins) lesson first.
+
+## Set Up
+Clone and check out the `joins-homework` branch of the [Set List Tutorial](https://github.com/turingschool-examples/set-list-7/tree/joins-homework). Run `bundle install` and `rails db:migrate`.
+
+## Joining Multiple Tables
+As you have learned, a Join query is what we use to combine data from at least two tables. If we push that idea further, it is also possible to join *many* tables together in order to gather information from multiple tables.
+
+In Set List, we have tables for Artists, Songs, and Playlists. Using a simple join query, we could get the names of artists that have songs with a play count greater than 400:
+
+```sql
+ SELECT DISTINCT artists.name FROM artists JOIN songs ON artists.id = songs.artist_id WHERE songs.play_count > 400;
+```
+
+If we run this query in Postico or `rails dbconsole`, we would see the names of a few artists. We add the `distinct` keyword because without it, we would see that some of those artists have multiple songs with a play count greater than 400.
+
+In ActiveRecord, we can write this query by joining on the Artist association to `:songs`:
+```ruby
+Artist.joins(:songs).where("songs.play_count > 400").distinct.pluck("artists.name")
+
+# Remember that we can use `.pluck` at the end of our queries to only grab the column we need in the format of an array.
+```
+
+But what about Playlists? What would the queries look like if we wanted to get the unique names of Artists from all Playlists?
+
+First, we know we could join Playlists to Songs, which is we have a join table already set up for.
+
+```ruby
+Playlist.joins(:songs)
+```
+Run this query and add `.to_sql`. What do you notice about the output?
+
+When we run this in ActiveRecord, it will first perform an inner join from `playlists` to `playlist_songs` (our join table), and then from there it can join `playlist_songs` to `songs`. But we're not quite there - we need information from the `artists` table too. And since we want to end up with Artist names, let's start our query with the `Artist` model:
+
+```ruby
+Artist.joins(songs: :playlists).distinct.pluck(:name)
+```
+
+This query looks a little funny, doesn't it? Why would we join like that? And what are those colons (`:`) doing? Read on...
+
+## Joining on Associations
+
+Whenever we are joining between multiple tables/models in ActiveRecord, it is important to remember that our associations are what *relate* our models together, in the same way that primary & foriegn keys between our database tables are what *relate* our tables together.
+
+Much like the stories of pirates following a treasure map, we should *follow the relationships* of our Models via their associations. If we start with a Playlist but want to end up with information from an Artist (or vice-versa), we should look at our model files and ask "which associations can get us there?".
+
+Let's take a look at the query we performed above, and break it down step-by-step:
+
+#### Prompt: Return the unique names of Artists from all Playlists.
+
+```ruby
+Artist.joins(songs: :playlists).distinct.pluck(:name)
+```
+
+1. We start with `Artist` because that's the kind of object we want returned.
+2. The `Artist` model has an association `has_many :songs`. So, since we join on associations, we start with `songs:` from the Artist model. However, the symbol is actually facing the `:playlists` association... An Artist doesn't have an association to playlists, but our `Song` model does! This join is doing 3 things:
+ 1. Joining `artists` to `songs`,
+ 2. then joining `songs` to `playlist_songs` through the join table,
+ 3. and finally joining `playlist_songs` to `playlists` through the join table.
+3. Then, we want unique records back and not duplicated artists, so we call `.distinct` on the result.
+4. Finally, we make an array of the `:name` attribute from the `artists` table (AR assumes it's from the Artist table because it will call that attribute on whatever model we started with; alternatively we can do `.pluck("artists.name")` instead).
+
+Try running the above query in your `rails console`. Did it work?
+
+Try running it again with `.to_sql` at the end. Can you follow the different joins that it creates?
+
+
+## Relying on Associations (or, The Easy Way)
+That query still has a lot of joins logic to it, though. Is there an easier way? You bet! Let's double-down on our love for associations and make this query possible:
+
+```ruby
+Artist.joins(:playlists).distinct.pluck(:name)
+```
+
+Right now, this query would not work, because an Artist does not yet have a *direct* association with the Playlist model. However, if we work through the logic in the above example, we can add that relationship as an association to the Artist model:
+
+**app/models/artist.rb**
+```ruby
+class Artist < ApplicationRecord
+ has_many :songs
+ has_many :playlists, through: :songs
+ #...
+```
+
+Logically, if an Artist has many Songs, and a Song has many Playlists, then we can tell the Artist that it `has_many :playlists, through: :songs` as a direct route to get to a collection of playlists from one artist!
+
+## AR without Associations (or, The Hard Way)
+
+If we don't make more associations, we can still make this query work. ActiveRecord syntax *can* make it a little difficult, though...
+
+For this example, we can start from Playlist and try to make it to Artist:
+
+```ruby
+Playlist.joins(playlist_songs: {song: :artist})
+# or
+Model.joins(x: {y: :z})
+```
+
+This syntax can also be read as:
+
+1. playlists joins playlist_songs (Model to "x")
+2. playlist_songs joins songs ("x" to "y")
+3. a song joins an artist ("y" to "z")
+
+The end of that explanation sounds a little odd - but remember in ActiveRecord we still **join on associations**, and a Song `belongs_to` one Artist.
+
+
+## Over-joining
+It is also possible to join many times in ActiveRecord using an array or comma syntax:
+
+```ruby
+Song.joins([:artist, :playlists])
+# or:
+Song.joins(:artist, :playlists)
+```
+
+In both of the above examples, AR generates this SQL for us (below). Follow along like a "treasure map" with this SQL statement:
+```sql
+SELECT "songs".* FROM "songs"
+ INNER JOIN "artists" ON "artists"."id" = "songs"."artist_id"
+ INNER JOIN "playlist_songs" ON "playlist_songs"."song_id" = "songs"."id"
+ INNER JOIN "playlists" ON "playlists"."id" = "playlist_songs"."playlist_id";
+```
+
+However, it is also possible to **over-join** when writing multiple joins:
+
+```ruby
+Song.joins([:playlist_songs, :playlists])
+```
+Run this query in `rails c`. Does it work?
+
+Then, run the query again with `.to_sql` at the end. What do you notice?
+
+The SQL that this query generates is:
+```sql
+SELECT "songs".* FROM "songs"
+ INNER JOIN "playlist_songs" ON "playlist_songs"."song_id" = "songs"."id"
+ INNER JOIN "playlist_songs" "playlist_songs_songs_join" ON "playlist_songs_songs_join"."song_id" = "songs"."id"
+ INNER JOIN "playlists" ON "playlists"."id" = "playlist_songs_songs_join"."playlist_id";
+```
+
+This syntax actually joins `playlist_songs` **twice**, since the `Song.joins(:playlists)` association already joins to `playlist_songs` by necessity.
+
+The takeaway here is, if we're not careful with our joins, it may result in some data being duplicated when a query returns its data. Remember that as developers we're responsible for the integrity of our data, and strong model tests around each model method we write will help to ensure we're returning exactly what we need, and no more.
+
+## Further Practice
+Try implementing some of these queries on your own. You may want to try writing them out in SQL first, before translating them to ActiveRecord.
+
+* Return a unique list of songs that appear on at least 1 playlist.
+* Return the names of the artists with songs on the "summer rewind" playlist. Use an additional association in the Artist model (i.e. join the "Easy Way").
+* Return the names of the artists with songs on the "summer rewind" playlist, but this time don't use an additional association (try it the "Hard Way").
+
+## Checks for Understanding
+1. When would we want to use multiple joins in a query?
+2. What is one hazard of potential over-joining?
+3. In your own words, describe the process for creating a multiple-join query.
+