SlideShare a Scribd company logo
Intermediate
SQL with Ecto
Dennis Beatty

@dnsbty
Intermediate SQL with Ecto - LoneStar ElixirConf 2018
Intermediate SQL with Ecto - LoneStar ElixirConf 2018
Intermediate SQL with Ecto - LoneStar ElixirConf 2018
Intermediate SQL with Ecto - LoneStar ElixirConf 2018
Intermediate SQL with Ecto - LoneStar ElixirConf 2018
GET https://mybusiness.googleapis.com/v4/
{parent=accounts/*/locations/*}/reviews
defmodule GMB.Reviews.Review do
use Ecto.Schema
schema "reviews" do
field :id, :string
field :author, :string
field :rating, :integer
field :reply, :string
field :created_at, :utc_datetime
field :updated_at, :utc_datetime
field :deleted_at, :utc_datetime
end
end
(ArgumentError) field/association :id
is already set on schema
defmodule GMB.Reviews.Review do
use Ecto.Schema
@primary_key {:id, :string, []}
schema "reviews" do
field :author, :string
field :comment, :string
field :rating, :integer
field :reply, :string
field :created_at, :utc_datetime
field :updated_at, :utc_datetime
field :deleted_at, :utc_datetime
end
end
defmodule GMB.Repo.Migrations.CreateReviews do
use Ecto.Migration
def change do
create table(:reviews) do
add :author, :string, null: false, default: ""
add :rating, :integer, null: false, default: 0
add :comment, :string, null: false, default: ""
add :reply, :string, null: false, default: ""
add :created_at, :naive_datetime, null: false, default: "epoch"
add :updated_at, :naive_datetime, null: false, default: "epoch"
add :deleted_at, :naive_datetime, null: false, default: "epoch"
end
end
end
defmodule GMB.Reviews.Review do
use Ecto.Schema
@primary_key {:id, :string, []}
schema "reviews" do
field :author, :string
field :comment, :string
field :rating, :integer
field :reply, :string
field :created_at, :utc_datetime
field :updated_at, :utc_datetime
field :deleted_at, :utc_datetime
end
end
defmodule GMB.Repo.Migrations.CreateReviews do
use Ecto.Migration
def change do
create table(:reviews, primary_key: false) do
add :id, :string, primary_key: true
add :author, :string, null: false, default: ""
add :rating, :integer, null: false, default: 0
add :comment, :string, null: false, default: ""
add :reply, :string, null: false, default: ""
add :created_at, :naive_datetime, null: false, default: "epoch"
add :updated_at, :naive_datetime, null: false, default: "epoch"
add :deleted_at, :naive_datetime, null: false, default: "epoch"
end
end
end
🎉
User
id - Integer
first_name - String
last_name - String
email - String
Group
id - Integer
name - String
description - String
slug - String
Member
user_id - Integer
group_id - Integer
role - String
defmodule SirAlex.Groups.Member do
use Ecto.Schema
alias SirAlex.Groups.{Group, User}
@primary_key false
schema "members" do
field :role, :string, default: "member"
belongs_to :group, Group
belongs_to :user, User
timestamps()
end
end
defmodule SirAlex.Repo.Migrations.CreateMembers do
use Ecto.Migration
def change do
create table(:members, primary_key: false) do
add :group_id, references(:groups), primary_key: true
add :user_id, references(:users), primary_key: true
add :role, :string, null: false, default: "member"
timestamps()
end
end
end
defmodule SirAlex.Accounts.User do
schema "users" do
...
many_to_many :groups, Group, join_through: Member
...
end
end
iex> user.groups
[%Group{}]
🎉
⭐⭐⭐⭐⭐
Intermediate SQL with Ecto - LoneStar ElixirConf 2018
defmodule GMB.Reviews do
alias GMB.Repo
alias GMB.Reviews.Review
def save_reviews(reviews) do
Enum.map(reviews, &save_review/1)
end
def save_review(attrs) do
%Review{}
|> Review.changeset(attrs)
|> Repo.insert()
end
end
INSERT INTO “reviews"
(“author”,"comment",
“created_at",“id",
"rating","reply")
VALUES
('Dennis','Elixir is awesome’,
‘2018-02-24T07:15:19.730704Z','s929u9dswrf9348',
5,'Yeah, it is!');
• Check out a DB connection from the pool

• Send review data to the DB

• Wait for response from DB

• Release the DB connection back to the pool
• Check out a DB connection from the pool

• Send review data to the DB

• Wait for response from DB

• Release the DB connection back to the pool
INSERT INTO “reviews"
(“author”,"comment",
“created_at",“id",
"rating","reply")
VALUES
('Dennis','Elixir is awesome’,
‘2018-02-24T07:15:19.730704Z','s929u9dswrf9348',
5,'Yeah, it is!’),
(‘Brett','Best BBQ place around.’,
‘2018-02-21T03:26:01.359302Z','sldfi304jslfi3l',
5,’Thanks!’);
defmodule GMB.Reviews do
alias GMB.Repo
alias GMB.Reviews.Review
def save_reviews(reviews) do
Enum.map(reviews, &save_review/1)
end
def save_review(attrs) do
%Review{}
|> Review.changeset(attrs)
|> Repo.insert()
end
end
defmodule GMB.Reviews do
alias GMB.Repo
alias GMB.Reviews.Review
def save_reviews(reviews) do
Repo.insert_all(Review, reviews)
end
end
No validation?! 😱
🎉
** (Postgrex.Error) ERROR 23505
(unique_violation): duplicate key
value violates unique constraint
"reviews_pkey"
Intermediate SQL with Ecto - LoneStar ElixirConf 2018
Intermediate SQL with Ecto - LoneStar ElixirConf 2018
def save_reviews(reviews) do
opts = [on_conflict: :nothing]
Repo.insert_all(Review, reviews, opts)
end
🎉
def save_reviews(reviews) do
opts = [on_conflict: :replace_all]
Repo.insert_all(Review, reviews, opts)
end
** (Postgrex.Error) ERROR 42601
(syntax_error): ON CONFLICT DO UPDATE
requires inference specification or
constraint name
def save_reviews(reviews) do
opts = [
on_conflict: :replace_all,
conflict_target: :id
]
Repo.insert_all(Review, reviews, opts)
end
🎉
def save_reviews(reviews) do
opts = [
on_conflict: [set: [comment: "updated"]],
conflict_target: :id
]
Repo.insert_all(Review, reviews, opts)
end
Intermediate SQL with Ecto - LoneStar ElixirConf 2018
😎
Intermediate SQL with Ecto - LoneStar ElixirConf 2018
def save_reviews(reviews) do
query = from r in Review, update: [set: [comment:
fragment("EXCLUDED.comment")]]
opts = [
on_conflict: query,
conflict_target: :id
]
Repo.insert_all(Review, reviews, opts)
end
🎉
https://goo.gl/DMof8Q
Under the Hood
• Get next val from sequence*

• Insert row with value from sequence

• If unique constraint error, run ON CONFLICT clause
reviews = [
%{
author: "Dennis",
comment: "Elixir is awesome",
created_at: #DateTime<2018-02-24 09:10:21.752944Z>,
id: "s929u9dswrf9348",
rating: 5,
reply: "Yeah, it is!"
},
%{
author: "Brett",
comment: "I wish I'd found Elixir sooner",
created_at: #DateTime<2018-02-24 09:10:21.754933Z>,
id: "wod938wlfu",
rating: 5,
reply: "Too bad"
}
]
iex> Reviews.save_reviews(reviews)
{2, nil}
def save_reviews(reviews) do
query = from r in Review, update: [set:
[comment: fragment("EXCLUDED.comment")]]
opts = [
on_conflict: query,
conflict_target: :id,
returning: true
]
Repo.insert_all(Review, reviews, opts)
end
iex> Reviews.save_reviews(reviews)
{2,
[
%GMB.Reviews.Review{
__meta__: #Ecto.Schema.Metadata<:loaded, "reviews">,
author: "Dennis",
comment: "Elixir is awesome",
created_at: #DateTime<2018-02-24 09:10:21.752944Z>,
deleted_at: #DateTime<1970-01-01 00:00:00.000000Z>,
id: "s929u9dswrf9348",
rating: 5,
reply: "Yeah, it is!",
updated_at: #DateTime<1970-01-01 00:00:00.000000Z>
},
%GMB.Reviews.Review{
__meta__: #Ecto.Schema.Metadata<:loaded, "reviews">,
author: "Brett",
comment: "I wish I'd found Elixir sooner",
created_at: #DateTime<2018-02-24 09:10:21.754933Z>,
deleted_at: #DateTime<1970-01-01 00:00:00.000000Z>,
id: "wod938wlfu",
rating: 5,
reply: "Too bad",
updated_at: #DateTime<1970-01-01 00:00:00.000000Z>
}
]}
podium.com/jobs
Further Reading
• Plataformatec - What’s New in Ecto 2.1

http://pages.plataformatec.com.br/ebook-whats-new-in-ecto-2-0

• Ecto Docs! 

https://hexdocs.pm/ecto/Ecto.html

• @dnsbty on twitter

More Related Content

KEY
Unit testing zend framework apps
KEY
Unit testing with zend framework PHPBenelux
PDF
Unit testing with zend framework tek11
PDF
Elixir formatter Internals
PDF
Refactoring using Codeception
PDF
Zf2 how arrays will save your project
PDF
QA for PHP projects
PPT
displaytag
Unit testing zend framework apps
Unit testing with zend framework PHPBenelux
Unit testing with zend framework tek11
Elixir formatter Internals
Refactoring using Codeception
Zf2 how arrays will save your project
QA for PHP projects
displaytag

What's hot (20)

PDF
Factory Girl
PPT
Zend framework 04 - forms
PDF
Leveraging Symfony2 Forms
PPTX
Introducing ExtReact: Adding Powerful Sencha Components to React Apps
PDF
Better Bullshit Driven Development [SeleniumCamp 2017]
PDF
2013-06-25 - HTML5 & JavaScript Security
PDF
Joomla! Components - Uma visão geral
KEY
Dsl
PDF
Design patterns in Magento
KEY
Workshop quality assurance for php projects tek12
PDF
Proposed PHP function: is_literal()
DOCX
20220112 sac v1
PPTX
Zero to SOLID
PPT
Moodle Quick Forms
PDF
UA testing with Selenium and PHPUnit - PFCongres 2013
PDF
Data Validation models
PPTX
WordPress overloading Gravityforms using hooks, filters and extending classes
PDF
Workshop quality assurance for php projects - ZendCon 2013
PDF
Writing DSLs with Parslet - Wicked Good Ruby Conf
Factory Girl
Zend framework 04 - forms
Leveraging Symfony2 Forms
Introducing ExtReact: Adding Powerful Sencha Components to React Apps
Better Bullshit Driven Development [SeleniumCamp 2017]
2013-06-25 - HTML5 & JavaScript Security
Joomla! Components - Uma visão geral
Dsl
Design patterns in Magento
Workshop quality assurance for php projects tek12
Proposed PHP function: is_literal()
20220112 sac v1
Zero to SOLID
Moodle Quick Forms
UA testing with Selenium and PHPUnit - PFCongres 2013
Data Validation models
WordPress overloading Gravityforms using hooks, filters and extending classes
Workshop quality assurance for php projects - ZendCon 2013
Writing DSLs with Parslet - Wicked Good Ruby Conf
Ad

Similar to Intermediate SQL with Ecto - LoneStar ElixirConf 2018 (20)

PDF
DataMapper @ RubyEnRails2009
PDF
Practical Ruby Projects with MongoDB - Ruby Kaigi 2010
PDF
Using Scala Slick at FortyTwo
PDF
Scala ActiveRecord
PDF
No sql databases blrdroid devfest 2016
KEY
Datamapper @ Railsconf2010
PDF
Where's My SQL? Designing Databases with ActiveRecord Migrations
PDF
DataMapper
PDF
ORM in Django
PDF
You got schema in my json
PDF
Intro to Cassandra and CassandraObject
PDF
Couchdb
PDF
The State of NoSQL
PDF
Scala active record
PDF
Elixir + Neo4j
PDF
Ruby meetup ROM
PDF
PofEAA and SQLAlchemy
PDF
Spring data requery
PDF
Юрий Буянов «Squeryl — ORM с человеческим лицом»
KEY
Practical Ruby Projects (Alex Sharp)
DataMapper @ RubyEnRails2009
Practical Ruby Projects with MongoDB - Ruby Kaigi 2010
Using Scala Slick at FortyTwo
Scala ActiveRecord
No sql databases blrdroid devfest 2016
Datamapper @ Railsconf2010
Where's My SQL? Designing Databases with ActiveRecord Migrations
DataMapper
ORM in Django
You got schema in my json
Intro to Cassandra and CassandraObject
Couchdb
The State of NoSQL
Scala active record
Elixir + Neo4j
Ruby meetup ROM
PofEAA and SQLAlchemy
Spring data requery
Юрий Буянов «Squeryl — ORM с человеческим лицом»
Practical Ruby Projects (Alex Sharp)
Ad

Recently uploaded (20)

PDF
Digital Systems & Binary Numbers (comprehensive )
PDF
PTS Company Brochure 2025 (1).pdf.......
PPTX
Introduction to Artificial Intelligence
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PPTX
Reimagine Home Health with the Power of Agentic AI​
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PDF
Digital Strategies for Manufacturing Companies
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Digital Systems & Binary Numbers (comprehensive )
PTS Company Brochure 2025 (1).pdf.......
Introduction to Artificial Intelligence
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
Navsoft: AI-Powered Business Solutions & Custom Software Development
How to Choose the Right IT Partner for Your Business in Malaysia
Odoo Companies in India – Driving Business Transformation.pdf
How to Migrate SBCGlobal Email to Yahoo Easily
Wondershare Filmora 15 Crack With Activation Key [2025
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
wealthsignaloriginal-com-DS-text-... (1).pdf
Reimagine Home Health with the Power of Agentic AI​
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Upgrade and Innovation Strategies for SAP ERP Customers
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Digital Strategies for Manufacturing Companies
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus

Intermediate SQL with Ecto - LoneStar ElixirConf 2018

  • 8. defmodule GMB.Reviews.Review do use Ecto.Schema schema "reviews" do field :id, :string field :author, :string field :rating, :integer field :reply, :string field :created_at, :utc_datetime field :updated_at, :utc_datetime field :deleted_at, :utc_datetime end end (ArgumentError) field/association :id is already set on schema
  • 9. defmodule GMB.Reviews.Review do use Ecto.Schema @primary_key {:id, :string, []} schema "reviews" do field :author, :string field :comment, :string field :rating, :integer field :reply, :string field :created_at, :utc_datetime field :updated_at, :utc_datetime field :deleted_at, :utc_datetime end end
  • 10. defmodule GMB.Repo.Migrations.CreateReviews do use Ecto.Migration def change do create table(:reviews) do add :author, :string, null: false, default: "" add :rating, :integer, null: false, default: 0 add :comment, :string, null: false, default: "" add :reply, :string, null: false, default: "" add :created_at, :naive_datetime, null: false, default: "epoch" add :updated_at, :naive_datetime, null: false, default: "epoch" add :deleted_at, :naive_datetime, null: false, default: "epoch" end end end
  • 11. defmodule GMB.Reviews.Review do use Ecto.Schema @primary_key {:id, :string, []} schema "reviews" do field :author, :string field :comment, :string field :rating, :integer field :reply, :string field :created_at, :utc_datetime field :updated_at, :utc_datetime field :deleted_at, :utc_datetime end end
  • 12. defmodule GMB.Repo.Migrations.CreateReviews do use Ecto.Migration def change do create table(:reviews, primary_key: false) do add :id, :string, primary_key: true add :author, :string, null: false, default: "" add :rating, :integer, null: false, default: 0 add :comment, :string, null: false, default: "" add :reply, :string, null: false, default: "" add :created_at, :naive_datetime, null: false, default: "epoch" add :updated_at, :naive_datetime, null: false, default: "epoch" add :deleted_at, :naive_datetime, null: false, default: "epoch" end end end
  • 13. 🎉
  • 14. User id - Integer first_name - String last_name - String email - String Group id - Integer name - String description - String slug - String Member user_id - Integer group_id - Integer role - String
  • 15. defmodule SirAlex.Groups.Member do use Ecto.Schema alias SirAlex.Groups.{Group, User} @primary_key false schema "members" do field :role, :string, default: "member" belongs_to :group, Group belongs_to :user, User timestamps() end end
  • 16. defmodule SirAlex.Repo.Migrations.CreateMembers do use Ecto.Migration def change do create table(:members, primary_key: false) do add :group_id, references(:groups), primary_key: true add :user_id, references(:users), primary_key: true add :role, :string, null: false, default: "member" timestamps() end end end
  • 17. defmodule SirAlex.Accounts.User do schema "users" do ... many_to_many :groups, Group, join_through: Member ... end end
  • 19. 🎉
  • 22. defmodule GMB.Reviews do alias GMB.Repo alias GMB.Reviews.Review def save_reviews(reviews) do Enum.map(reviews, &save_review/1) end def save_review(attrs) do %Review{} |> Review.changeset(attrs) |> Repo.insert() end end
  • 23. INSERT INTO “reviews" (“author”,"comment", “created_at",“id", "rating","reply") VALUES ('Dennis','Elixir is awesome’, ‘2018-02-24T07:15:19.730704Z','s929u9dswrf9348', 5,'Yeah, it is!');
  • 24. • Check out a DB connection from the pool • Send review data to the DB • Wait for response from DB • Release the DB connection back to the pool
  • 25. • Check out a DB connection from the pool • Send review data to the DB • Wait for response from DB • Release the DB connection back to the pool
  • 26. INSERT INTO “reviews" (“author”,"comment", “created_at",“id", "rating","reply") VALUES ('Dennis','Elixir is awesome’, ‘2018-02-24T07:15:19.730704Z','s929u9dswrf9348', 5,'Yeah, it is!’), (‘Brett','Best BBQ place around.’, ‘2018-02-21T03:26:01.359302Z','sldfi304jslfi3l', 5,’Thanks!’);
  • 27. defmodule GMB.Reviews do alias GMB.Repo alias GMB.Reviews.Review def save_reviews(reviews) do Enum.map(reviews, &save_review/1) end def save_review(attrs) do %Review{} |> Review.changeset(attrs) |> Repo.insert() end end
  • 28. defmodule GMB.Reviews do alias GMB.Repo alias GMB.Reviews.Review def save_reviews(reviews) do Repo.insert_all(Review, reviews) end end No validation?! 😱
  • 29. 🎉
  • 30. ** (Postgrex.Error) ERROR 23505 (unique_violation): duplicate key value violates unique constraint "reviews_pkey"
  • 33. def save_reviews(reviews) do opts = [on_conflict: :nothing] Repo.insert_all(Review, reviews, opts) end
  • 34. 🎉
  • 35. def save_reviews(reviews) do opts = [on_conflict: :replace_all] Repo.insert_all(Review, reviews, opts) end ** (Postgrex.Error) ERROR 42601 (syntax_error): ON CONFLICT DO UPDATE requires inference specification or constraint name
  • 36. def save_reviews(reviews) do opts = [ on_conflict: :replace_all, conflict_target: :id ] Repo.insert_all(Review, reviews, opts) end
  • 37. 🎉
  • 38. def save_reviews(reviews) do opts = [ on_conflict: [set: [comment: "updated"]], conflict_target: :id ] Repo.insert_all(Review, reviews, opts) end
  • 40. 😎
  • 42. def save_reviews(reviews) do query = from r in Review, update: [set: [comment: fragment("EXCLUDED.comment")]] opts = [ on_conflict: query, conflict_target: :id ] Repo.insert_all(Review, reviews, opts) end
  • 43. 🎉
  • 45. Under the Hood • Get next val from sequence* • Insert row with value from sequence • If unique constraint error, run ON CONFLICT clause
  • 46. reviews = [ %{ author: "Dennis", comment: "Elixir is awesome", created_at: #DateTime<2018-02-24 09:10:21.752944Z>, id: "s929u9dswrf9348", rating: 5, reply: "Yeah, it is!" }, %{ author: "Brett", comment: "I wish I'd found Elixir sooner", created_at: #DateTime<2018-02-24 09:10:21.754933Z>, id: "wod938wlfu", rating: 5, reply: "Too bad" } ]
  • 48. def save_reviews(reviews) do query = from r in Review, update: [set: [comment: fragment("EXCLUDED.comment")]] opts = [ on_conflict: query, conflict_target: :id, returning: true ] Repo.insert_all(Review, reviews, opts) end
  • 49. iex> Reviews.save_reviews(reviews) {2, [ %GMB.Reviews.Review{ __meta__: #Ecto.Schema.Metadata<:loaded, "reviews">, author: "Dennis", comment: "Elixir is awesome", created_at: #DateTime<2018-02-24 09:10:21.752944Z>, deleted_at: #DateTime<1970-01-01 00:00:00.000000Z>, id: "s929u9dswrf9348", rating: 5, reply: "Yeah, it is!", updated_at: #DateTime<1970-01-01 00:00:00.000000Z> }, %GMB.Reviews.Review{ __meta__: #Ecto.Schema.Metadata<:loaded, "reviews">, author: "Brett", comment: "I wish I'd found Elixir sooner", created_at: #DateTime<2018-02-24 09:10:21.754933Z>, deleted_at: #DateTime<1970-01-01 00:00:00.000000Z>, id: "wod938wlfu", rating: 5, reply: "Too bad", updated_at: #DateTime<1970-01-01 00:00:00.000000Z> } ]}
  • 51. Further Reading • Plataformatec - What’s New in Ecto 2.1
 http://pages.plataformatec.com.br/ebook-whats-new-in-ecto-2-0 • Ecto Docs! 
 https://hexdocs.pm/ecto/Ecto.html • @dnsbty on twitter