SlideShare a Scribd company logo
1 of 25
Download to read offline
PostgreSQL 
Materialized Views 
and 
Active Record 
David Roberts
The Problem 
How do you quickly filter data 
represented by multiple ActiveRecord 
associations and calculations?
Data Model 
City% 
&%Philly% 
&%Boston% 
Technology% 
&%Ruby% 
&%Python% 
Club% 
&%Philly.rb% 
Talk% 
&%PostgreSQL% 
Materialized%Views% 
Feedback% 
&%“Super%rad%talk!!”% 
&%“The%whole%world%is% 
now%dumber”% 
Author% 
&%David%Roberts%
View all Comments 
class Feedback < ActiveRecord::Base 
belongs_to :talk 
INVALID_COMMENTS = ['', 'NA', 'N/A', 'not 
applicable'] 
scope :filled_out, 
-> { where.not(comment: INVALID_COMMENTS) } 
end 
Feedback.filled_out 
Feedback Load (2409.6ms) SELECT "feedbacks".* 
FROM "feedbacks" WHERE ("feedbacks"."comment" 
NOT IN ('', 'NA', 'N/A', 'not applicable'))
Highest Scoring Talk with 
Valid Comments 
Feedback.filled_out  
.select('talk_id, avg(score) as overall_score')  
.group('talk_id').order('overall_score desc')  
.limit(10) 
# 665ms 
SELECT talk_id, avg(score) as overall_score FROM 
"feedbacks" 
WHERE ("feedbacks"."comment" NOT IN ('', 'NA', 'N/ 
A', 'not applicable')) 
GROUP BY talk_id ORDER BY overall_score desc LIMIT 
10;
Highest Scoring Talk with 
Valid Comments 
results = Feedback.filled_out  
.select('talk_id, avg(score) as overall_score')  
.group('talk_id').order('overall_score desc')  
.limit(10) 
results.first.inspect 
=> "#<Feedback id: nil, talk_id: 24510>"
Highest Scoring Talks in PA 
by Authors named Parker 
Feedback.filled_out.joins(talk: [:author, 
{ club: :city }] )  
.select('feedbacks.talk_id, 
avg(feedbacks.score) as overall_score')  
.where("cities.state_abbr = ?", 'PA')  
.where("authors.name LIKE '%?%'", 'Parker')  
.group('feedbacks.talk_id')  
.order('overall_score desc')  
.limit(10) 
# 665ms 
SELECT feedbacks.talk_id, avg(feedbacks.score) as overall_score FROM "feedbacks" 
INNER JOIN "talks" ON "talks"."id" = "feedbacks"."talk_id" 
INNER JOIN "authors" ON "authors"."id" = "talks"."author_id" 
INNER JOIN "clubs" ON "clubs"."id" = "talks"."club_id" 
INNER JOIN "cities" ON "cities"."id" = "clubs"."city_id" 
WHERE ("feedbacks"."comment" NOT IN ('', 'NA', 'N/A', 'not applicable')) 
AND (cities.state_abbr = 'PA') 
AND (authors.name LIKE '%Parker%') 
GROUP BY feedbacks.talk_id ORDER BY overall_score desc LIMIT 10;
What’s Wrong with these 
Examples? 
• Long ugly queries 
• Slow queries are bad for Web Applications 
• Fighting ActiveRecord framework 
• SQL aggregates are difficult to access 
• Returned object no longer corresponds to model 
• Repetitive code to setup joins and Filter invalid 
comments
Using 
Database Views 
to clean up 
ActiveRecord models
Views 
• Uses a stored / pre-defined Query 
• Uses live data from corresponding tables when 
queried 
• Can reference data from many tables 
• Great for hiding complex SQL statements 
• Allows you to push functionality to database
But this is a Ruby talk!
class CreateTalkView < ActiveRecord::Migration 
def up 
connection.execute <<-SQL 
CREATE VIEW v_talks_report AS 
SELECT cities.id as city_id, 
cities.name as city_name, 
cities.state_abbr as state_abbr, 
technologies.id as technology_id, 
clubs.id as club_id, 
clubs.name as club_name, 
talks.id as talk_id, 
talks.name as talk_name, 
authors.id as author_id, 
authors.name as author_name, 
feedback_agg.overall_score as overall_score 
FROM ( 
SELECT talk_id, avg(score) as overall_score 
FROM feedbacks 
WHERE feedbacks.comment NOT IN ('', 'NA', 'N/A', 'not applicable') 
GROUP BY talk_id 
) as feedback_agg 
INNER JOIN talks ON feedback_agg.talk_id = talks.id 
INNER JOIN authors ON talks.author_id = authors.id 
INNER JOIN clubs ON talks.club_id = clubs.id 
INNER JOIN cities ON clubs.city_id = cities.id 
INNER JOIN technologies ON clubs.technology_id = technologies.id 
SQL 
end 
def down 
connection.execute 'DROP VIEW IF EXISTS v_talks_report' 
end 
end
Encapsulate in ActiveRecord Model 
class TalkReport < ActiveRecord::Base 
# Use associations just like any other ActiveRecord 
object 
belongs_to :author 
belongs_to :talk 
belongs_to :club 
belongs_to :city 
belongs_to :technology 
# take advantage of talks has_many relationship 
delegate :feedbacks, to: :talk 
self.table_name = 'v_talks_report' 
# views cannot be changed since they are virtual 
def readonly 
true 
end 
end
Highest Scoring Talks with Valid Comments 
results = TalkReport.order(overall_score: :desc)  
.limit(10) 
results.first.inspect 
=> "#<TalkReport city_id: 16, city_name: 
"Burlington", state_abbr: "VT", technology_id: 2, 
club_id: 73, club_name: "Python - Burlington", 
talk_id: 6508, talk_name: "The SQL protocol is down, 
reboot the optical syste...", author_id: 100, 
author_name: "Jerrell Gleichner", overall_score: 
#<BigDecimal:7ff3f80f1678,'0.4E1',9(27)>>"
Highest Scoring Talks in PA by Authors 
named Parker 
TalkReport.where(state_abbr: 'PA')  
.where("author_name LIKE '%Parker%'")  
.order(overall_score: :desc).limit(10)
Using 
Materialized Views 
to Improve Performance
Materialized Views 
• Acts similar to a Database View, but results persist for 
future queries 
• Creates a table on disk with the Result set 
• Can be indexed 
• Ideal for capturing frequently used joins and aggregations 
• Allows optimization of tables for updating and Materialized 
Views for reporting 
• Must be refreshed to be updated with most recent data
class CreateTalkReportMv < ActiveRecord::Migration 
def up 
connection.execute <<-SQL 
CREATE MATERIALIZED VIEW mv_talks_report AS 
SELECT cities.id as city_id, 
cities.name as city_name, 
cities.state_abbr as state_abbr, 
technologies.id as technology_id, 
clubs.id as club_id, 
clubs.name as club_name, 
talks.id as talk_id, 
talks.name as talk_name, 
authors.id as author_id, 
authors.name as author_name, 
feedback_agg.overall_score as overall_score 
FROM ( 
SELECT talk_id, avg(score) as overall_score 
FROM feedbacks 
WHERE feedbacks.comment NOT IN ('', 'NA', 'N/A', 'not applicable') 
GROUP BY talk_id 
) as feedback_agg 
INNER JOIN talks ON feedback_agg.talk_id = talks.id 
INNER JOIN authors ON talks.author_id = authors.id 
INNER JOIN clubs ON talks.club_id = clubs.id 
INNER JOIN cities ON clubs.city_id = cities.id 
INNER JOIN technologies ON clubs.technology_id = technologies.id; 
CREATE INDEX ON mv_talks_report (overall_score); 
SQL 
end 
def down 
connection.execute 'DROP MATERIALIZED VIEW IF EXISTS mv_talks_report' 
end 
end
ActiveRecord Model Change 
class TalkReport < ActiveRecord::Base 
# No changes to associations 
belongs_to … 
self.table_name = 'mv_talks_report' 
def self.repopulate 
connection.execute("REFRESH MATERIALIZED VIEW #{table_name}") 
end 
# Materialized Views cannot be modified 
def readonly 
true 
end 
end
Highest Scoring Talks 
99% reduction in runtime 
577ms 
Feedback.filled_out  
.select('talk_id, avg(score) as score')  
.group('talk_id').order('score desc').limit(10) 
1ms 
TalkReport.order(overall_score: :desc).limit(10)
Highest Scoring Talks in PA 
by Authors named “Parker” 
95% reduction in runtime 
400ms 
Feedback.filled_out.joins(talk: [:author, { club: :city }] )  
.select('feedbacks.talk_id, avg(feedbacks.score) as 
overall_score')  
.where("cities.state_abbr = ?", 'PA')  
.where("authors.name LIKE '%?%'", 'Parker')  
.group('feedbacks.talk_id')  
.order('overall_score desc')  
.limit(10) 
19ms 
TalkReport.where(state_abbr: 'PA')  
.where("author_name LIKE '%?%'", 'Parker')  
.order(overall_score: :desc).limit(10)
Why Use Materialized Views 
in your Rails application? 
• ActiveRecord models allow for easy representation in Rails 
• Capture commonly used joins / filters 
• Allows for fast, live filtering and sorting of complex associations 
or calculated fields 
• Push data intensive processing out of Ruby to Database 
• Make use of advanced Database functions 
• Can Optimize Indexes for reporting only 
• When Performance is more important than Storage
Downsides 
• Requires PostgreSQL 9.3 
• Entire Materialized View must be refreshed to 
update 
• Bad when Live Data is required 
• For this use case, roll your own Materialized 
View using standard tables
Downsides 
• Migrations are painful! 
• Recommend writing in SQL, so no using 
scopes 
• Entire Materialized View must be dropped and 
redefined for any changes to the View or 
referring tables 
• Hard to read and track what changed
Resources 
• Source Code used in talk 
• https://github.com/droberts84/materialized-view- 
demo 
• PostgreSQL Documentation 
• https://wiki.postgresql.org/wiki/ 
Materialized_Views

More Related Content

What's hot

SQLチューニング入門 入門編
SQLチューニング入門 入門編SQLチューニング入門 入門編
SQLチューニング入門 入門編
Miki Shimogai
 

What's hot (20)

Maria db 이중화구성_고민하기
Maria db 이중화구성_고민하기Maria db 이중화구성_고민하기
Maria db 이중화구성_고민하기
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
 
New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)
New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)
New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)
 
PostgreSQL 12は ここがスゴイ! ~性能改善やpluggable storage engineなどの新機能を徹底解説~ (NTTデータ テクノ...
PostgreSQL 12は ここがスゴイ! ~性能改善やpluggable storage engineなどの新機能を徹底解説~ (NTTデータ テクノ...PostgreSQL 12は ここがスゴイ! ~性能改善やpluggable storage engineなどの新機能を徹底解説~ (NTTデータ テクノ...
PostgreSQL 12は ここがスゴイ! ~性能改善やpluggable storage engineなどの新機能を徹底解説~ (NTTデータ テクノ...
 
雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング
 
RLSを用いたマルチテナント実装 for Django
RLSを用いたマルチテナント実装 for DjangoRLSを用いたマルチテナント実装 for Django
RLSを用いたマルチテナント実装 for Django
 
オンライン物理バックアップの排他モードと非排他モードについて ~PostgreSQLバージョン15対応版~(第34回PostgreSQLアンカンファレンス...
オンライン物理バックアップの排他モードと非排他モードについて ~PostgreSQLバージョン15対応版~(第34回PostgreSQLアンカンファレンス...オンライン物理バックアップの排他モードと非排他モードについて ~PostgreSQLバージョン15対応版~(第34回PostgreSQLアンカンファレンス...
オンライン物理バックアップの排他モードと非排他モードについて ~PostgreSQLバージョン15対応版~(第34回PostgreSQLアンカンファレンス...
 
jooqってなんて読むの? から始めるO/RマッパーとSpringBootの世界
jooqってなんて読むの? から始めるO/RマッパーとSpringBootの世界jooqってなんて読むの? から始めるO/RマッパーとSpringBootの世界
jooqってなんて読むの? から始めるO/RマッパーとSpringBootの世界
 
ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説
 
やってはいけない空振りDelete
やってはいけない空振りDeleteやってはいけない空振りDelete
やってはいけない空振りDelete
 
Using galera replication to create geo distributed clusters on the wan
Using galera replication to create geo distributed clusters on the wanUsing galera replication to create geo distributed clusters on the wan
Using galera replication to create geo distributed clusters on the wan
 
行ロックと「LOG: process 12345 still waiting for ShareLock on transaction 710 afte...
行ロックと「LOG:  process 12345 still waiting for ShareLock on transaction 710 afte...行ロックと「LOG:  process 12345 still waiting for ShareLock on transaction 710 afte...
行ロックと「LOG: process 12345 still waiting for ShareLock on transaction 710 afte...
 
JavaでWebサービスを作り続けるための戦略と戦術 JJUG-CCC-2018-Spring-g1
JavaでWebサービスを作り続けるための戦略と戦術 JJUG-CCC-2018-Spring-g1JavaでWebサービスを作り続けるための戦略と戦術 JJUG-CCC-2018-Spring-g1
JavaでWebサービスを作り続けるための戦略と戦術 JJUG-CCC-2018-Spring-g1
 
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)
 
High Availability PostgreSQL with Zalando Patroni
High Availability PostgreSQL with Zalando PatroniHigh Availability PostgreSQL with Zalando Patroni
High Availability PostgreSQL with Zalando Patroni
 
SQLチューニング入門 入門編
SQLチューニング入門 入門編SQLチューニング入門 入門編
SQLチューニング入門 入門編
 
いまさら聞けないPostgreSQL運用管理
いまさら聞けないPostgreSQL運用管理いまさら聞けないPostgreSQL運用管理
いまさら聞けないPostgreSQL運用管理
 
PostgreSQL 15の新機能を徹底解説
PostgreSQL 15の新機能を徹底解説PostgreSQL 15の新機能を徹底解説
PostgreSQL 15の新機能を徹底解説
 
PostgreSQL replication
PostgreSQL replicationPostgreSQL replication
PostgreSQL replication
 
イルカさんチームからゾウさんチームに教えたいMySQLレプリケーション
イルカさんチームからゾウさんチームに教えたいMySQLレプリケーションイルカさんチームからゾウさんチームに教えたいMySQLレプリケーション
イルカさんチームからゾウさんチームに教えたいMySQLレプリケーション
 

Viewers also liked

Introduction to execution plan analysis
Introduction to execution plan analysisIntroduction to execution plan analysis
Introduction to execution plan analysis
John Sterrett
 
Using puppet, foreman and git to develop and operate a large scale internet s...
Using puppet, foreman and git to develop and operate a large scale internet s...Using puppet, foreman and git to develop and operate a large scale internet s...
Using puppet, foreman and git to develop and operate a large scale internet s...
techblog
 
Sql query tuning or query optimization
Sql query tuning or query optimizationSql query tuning or query optimization
Sql query tuning or query optimization
Vivek Singh
 
vSphere APIs for performance monitoring
vSphere APIs for performance monitoringvSphere APIs for performance monitoring
vSphere APIs for performance monitoring
Alan Renouf
 
Redis — The AK-47 of Post-relational Databases
Redis — The AK-47 of Post-relational DatabasesRedis — The AK-47 of Post-relational Databases
Redis — The AK-47 of Post-relational Databases
Karel Minarik
 

Viewers also liked (20)

Materialized views in PostgreSQL
Materialized views in PostgreSQLMaterialized views in PostgreSQL
Materialized views in PostgreSQL
 
Part1 materialized view
Part1 materialized viewPart1 materialized view
Part1 materialized view
 
Cloud architectural patterns and Microsoft Azure tools
Cloud architectural patterns and Microsoft Azure toolsCloud architectural patterns and Microsoft Azure tools
Cloud architectural patterns and Microsoft Azure tools
 
Part 25 view vs. mv
Part 25 view vs. mvPart 25 view vs. mv
Part 25 view vs. mv
 
Introduction to execution plan analysis
Introduction to execution plan analysisIntroduction to execution plan analysis
Introduction to execution plan analysis
 
Using puppet, foreman and git to develop and operate a large scale internet s...
Using puppet, foreman and git to develop and operate a large scale internet s...Using puppet, foreman and git to develop and operate a large scale internet s...
Using puppet, foreman and git to develop and operate a large scale internet s...
 
Mapping, Interlinking and Exposing MusicBrainz as Linked Data
Mapping, Interlinking and Exposing MusicBrainz as Linked DataMapping, Interlinking and Exposing MusicBrainz as Linked Data
Mapping, Interlinking and Exposing MusicBrainz as Linked Data
 
Sql query tuning or query optimization
Sql query tuning or query optimizationSql query tuning or query optimization
Sql query tuning or query optimization
 
Continuously-Integrated Puppet in a Dynamic Environment
Continuously-Integrated Puppet in a Dynamic EnvironmentContinuously-Integrated Puppet in a Dynamic Environment
Continuously-Integrated Puppet in a Dynamic Environment
 
Lect 08 materialized view
Lect 08 materialized viewLect 08 materialized view
Lect 08 materialized view
 
JSON and the APInauts
JSON and the APInautsJSON and the APInauts
JSON and the APInauts
 
Better encryption & security with MariaDB 10.1 & MySQL 5.7
Better encryption & security with MariaDB 10.1 & MySQL 5.7Better encryption & security with MariaDB 10.1 & MySQL 5.7
Better encryption & security with MariaDB 10.1 & MySQL 5.7
 
Ruby application based on http
Ruby application based on httpRuby application based on http
Ruby application based on http
 
Sensu
SensuSensu
Sensu
 
vSphere APIs for performance monitoring
vSphere APIs for performance monitoringvSphere APIs for performance monitoring
vSphere APIs for performance monitoring
 
Dlsecyx pgroammr (Dyslexic Programmer - cool stuff for scaling)
Dlsecyx pgroammr (Dyslexic Programmer - cool stuff for scaling)Dlsecyx pgroammr (Dyslexic Programmer - cool stuff for scaling)
Dlsecyx pgroammr (Dyslexic Programmer - cool stuff for scaling)
 
The Complete MariaDB Server Tutorial - Percona Live 2015
The Complete MariaDB Server Tutorial - Percona Live 2015The Complete MariaDB Server Tutorial - Percona Live 2015
The Complete MariaDB Server Tutorial - Percona Live 2015
 
Redis — The AK-47 of Post-relational Databases
Redis — The AK-47 of Post-relational DatabasesRedis — The AK-47 of Post-relational Databases
Redis — The AK-47 of Post-relational Databases
 
Taking Control of Chaos with Docker and Puppet
Taking Control of Chaos with Docker and PuppetTaking Control of Chaos with Docker and Puppet
Taking Control of Chaos with Docker and Puppet
 
Detecting headless browsers
Detecting headless browsersDetecting headless browsers
Detecting headless browsers
 

Similar to PostgreSQL Materialized Views with Active Record

Intro to node and mongodb 1
Intro to node and mongodb   1Intro to node and mongodb   1
Intro to node and mongodb 1
Mohammad Qureshi
 
Intro to-rails-webperf
Intro to-rails-webperfIntro to-rails-webperf
Intro to-rails-webperf
New Relic
 

Similar to PostgreSQL Materialized Views with Active Record (20)

SF Elixir Meetup - RethinkDB
SF Elixir Meetup - RethinkDBSF Elixir Meetup - RethinkDB
SF Elixir Meetup - RethinkDB
 
mashraqi_farhan
mashraqi_farhanmashraqi_farhan
mashraqi_farhan
 
MuleSoft London Community February 2020 - MuleSoft and OData
MuleSoft London Community February 2020 - MuleSoft and ODataMuleSoft London Community February 2020 - MuleSoft and OData
MuleSoft London Community February 2020 - MuleSoft and OData
 
Change RelationalDB to GraphDB with OrientDB
Change RelationalDB to GraphDB with OrientDBChange RelationalDB to GraphDB with OrientDB
Change RelationalDB to GraphDB with OrientDB
 
MongoDB 3.4 webinar
MongoDB 3.4 webinarMongoDB 3.4 webinar
MongoDB 3.4 webinar
 
Keynote - Speaker: Grigori Melnik
Keynote - Speaker: Grigori Melnik Keynote - Speaker: Grigori Melnik
Keynote - Speaker: Grigori Melnik
 
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
 
Rails antipattern-public
Rails antipattern-publicRails antipattern-public
Rails antipattern-public
 
Intro to node and mongodb 1
Intro to node and mongodb   1Intro to node and mongodb   1
Intro to node and mongodb 1
 
Rails antipatterns
Rails antipatternsRails antipatterns
Rails antipatterns
 
How sitecore depends on mongo db for scalability and performance, and what it...
How sitecore depends on mongo db for scalability and performance, and what it...How sitecore depends on mongo db for scalability and performance, and what it...
How sitecore depends on mongo db for scalability and performance, and what it...
 
Simplifying & accelerating application development with MongoDB's intelligent...
Simplifying & accelerating application development with MongoDB's intelligent...Simplifying & accelerating application development with MongoDB's intelligent...
Simplifying & accelerating application development with MongoDB's intelligent...
 
Wider than rails
Wider than railsWider than rails
Wider than rails
 
Intro to-rails-webperf
Intro to-rails-webperfIntro to-rails-webperf
Intro to-rails-webperf
 
MongoDB Meetup
MongoDB MeetupMongoDB Meetup
MongoDB Meetup
 
MongoDB.local DC 2018: Tutorial - Data Analytics with MongoDB
MongoDB.local DC 2018: Tutorial - Data Analytics with MongoDBMongoDB.local DC 2018: Tutorial - Data Analytics with MongoDB
MongoDB.local DC 2018: Tutorial - Data Analytics with MongoDB
 
Intro to Rails ActiveRecord
Intro to Rails ActiveRecordIntro to Rails ActiveRecord
Intro to Rails ActiveRecord
 
Works with persistent graphs using OrientDB
Works with persistent graphs using OrientDB Works with persistent graphs using OrientDB
Works with persistent graphs using OrientDB
 
Schedule based network orchestration using opendaylight
Schedule based network orchestration using opendaylightSchedule based network orchestration using opendaylight
Schedule based network orchestration using opendaylight
 
Ibm_interconnect_restapi_workshop
Ibm_interconnect_restapi_workshopIbm_interconnect_restapi_workshop
Ibm_interconnect_restapi_workshop
 

Recently uploaded

%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
masabamasaba
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
masabamasaba
 

Recently uploaded (20)

%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
Chinsurah Escorts ☎️8617697112 Starting From 5K to 15K High Profile Escorts ...
Chinsurah Escorts ☎️8617697112  Starting From 5K to 15K High Profile Escorts ...Chinsurah Escorts ☎️8617697112  Starting From 5K to 15K High Profile Escorts ...
Chinsurah Escorts ☎️8617697112 Starting From 5K to 15K High Profile Escorts ...
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 

PostgreSQL Materialized Views with Active Record

  • 1. PostgreSQL Materialized Views and Active Record David Roberts
  • 2. The Problem How do you quickly filter data represented by multiple ActiveRecord associations and calculations?
  • 3. Data Model City% &%Philly% &%Boston% Technology% &%Ruby% &%Python% Club% &%Philly.rb% Talk% &%PostgreSQL% Materialized%Views% Feedback% &%“Super%rad%talk!!”% &%“The%whole%world%is% now%dumber”% Author% &%David%Roberts%
  • 4. View all Comments class Feedback < ActiveRecord::Base belongs_to :talk INVALID_COMMENTS = ['', 'NA', 'N/A', 'not applicable'] scope :filled_out, -> { where.not(comment: INVALID_COMMENTS) } end Feedback.filled_out Feedback Load (2409.6ms) SELECT "feedbacks".* FROM "feedbacks" WHERE ("feedbacks"."comment" NOT IN ('', 'NA', 'N/A', 'not applicable'))
  • 5. Highest Scoring Talk with Valid Comments Feedback.filled_out .select('talk_id, avg(score) as overall_score') .group('talk_id').order('overall_score desc') .limit(10) # 665ms SELECT talk_id, avg(score) as overall_score FROM "feedbacks" WHERE ("feedbacks"."comment" NOT IN ('', 'NA', 'N/ A', 'not applicable')) GROUP BY talk_id ORDER BY overall_score desc LIMIT 10;
  • 6. Highest Scoring Talk with Valid Comments results = Feedback.filled_out .select('talk_id, avg(score) as overall_score') .group('talk_id').order('overall_score desc') .limit(10) results.first.inspect => "#<Feedback id: nil, talk_id: 24510>"
  • 7. Highest Scoring Talks in PA by Authors named Parker Feedback.filled_out.joins(talk: [:author, { club: :city }] ) .select('feedbacks.talk_id, avg(feedbacks.score) as overall_score') .where("cities.state_abbr = ?", 'PA') .where("authors.name LIKE '%?%'", 'Parker') .group('feedbacks.talk_id') .order('overall_score desc') .limit(10) # 665ms SELECT feedbacks.talk_id, avg(feedbacks.score) as overall_score FROM "feedbacks" INNER JOIN "talks" ON "talks"."id" = "feedbacks"."talk_id" INNER JOIN "authors" ON "authors"."id" = "talks"."author_id" INNER JOIN "clubs" ON "clubs"."id" = "talks"."club_id" INNER JOIN "cities" ON "cities"."id" = "clubs"."city_id" WHERE ("feedbacks"."comment" NOT IN ('', 'NA', 'N/A', 'not applicable')) AND (cities.state_abbr = 'PA') AND (authors.name LIKE '%Parker%') GROUP BY feedbacks.talk_id ORDER BY overall_score desc LIMIT 10;
  • 8. What’s Wrong with these Examples? • Long ugly queries • Slow queries are bad for Web Applications • Fighting ActiveRecord framework • SQL aggregates are difficult to access • Returned object no longer corresponds to model • Repetitive code to setup joins and Filter invalid comments
  • 9. Using Database Views to clean up ActiveRecord models
  • 10. Views • Uses a stored / pre-defined Query • Uses live data from corresponding tables when queried • Can reference data from many tables • Great for hiding complex SQL statements • Allows you to push functionality to database
  • 11. But this is a Ruby talk!
  • 12. class CreateTalkView < ActiveRecord::Migration def up connection.execute <<-SQL CREATE VIEW v_talks_report AS SELECT cities.id as city_id, cities.name as city_name, cities.state_abbr as state_abbr, technologies.id as technology_id, clubs.id as club_id, clubs.name as club_name, talks.id as talk_id, talks.name as talk_name, authors.id as author_id, authors.name as author_name, feedback_agg.overall_score as overall_score FROM ( SELECT talk_id, avg(score) as overall_score FROM feedbacks WHERE feedbacks.comment NOT IN ('', 'NA', 'N/A', 'not applicable') GROUP BY talk_id ) as feedback_agg INNER JOIN talks ON feedback_agg.talk_id = talks.id INNER JOIN authors ON talks.author_id = authors.id INNER JOIN clubs ON talks.club_id = clubs.id INNER JOIN cities ON clubs.city_id = cities.id INNER JOIN technologies ON clubs.technology_id = technologies.id SQL end def down connection.execute 'DROP VIEW IF EXISTS v_talks_report' end end
  • 13. Encapsulate in ActiveRecord Model class TalkReport < ActiveRecord::Base # Use associations just like any other ActiveRecord object belongs_to :author belongs_to :talk belongs_to :club belongs_to :city belongs_to :technology # take advantage of talks has_many relationship delegate :feedbacks, to: :talk self.table_name = 'v_talks_report' # views cannot be changed since they are virtual def readonly true end end
  • 14. Highest Scoring Talks with Valid Comments results = TalkReport.order(overall_score: :desc) .limit(10) results.first.inspect => "#<TalkReport city_id: 16, city_name: "Burlington", state_abbr: "VT", technology_id: 2, club_id: 73, club_name: "Python - Burlington", talk_id: 6508, talk_name: "The SQL protocol is down, reboot the optical syste...", author_id: 100, author_name: "Jerrell Gleichner", overall_score: #<BigDecimal:7ff3f80f1678,'0.4E1',9(27)>>"
  • 15. Highest Scoring Talks in PA by Authors named Parker TalkReport.where(state_abbr: 'PA') .where("author_name LIKE '%Parker%'") .order(overall_score: :desc).limit(10)
  • 16. Using Materialized Views to Improve Performance
  • 17. Materialized Views • Acts similar to a Database View, but results persist for future queries • Creates a table on disk with the Result set • Can be indexed • Ideal for capturing frequently used joins and aggregations • Allows optimization of tables for updating and Materialized Views for reporting • Must be refreshed to be updated with most recent data
  • 18. class CreateTalkReportMv < ActiveRecord::Migration def up connection.execute <<-SQL CREATE MATERIALIZED VIEW mv_talks_report AS SELECT cities.id as city_id, cities.name as city_name, cities.state_abbr as state_abbr, technologies.id as technology_id, clubs.id as club_id, clubs.name as club_name, talks.id as talk_id, talks.name as talk_name, authors.id as author_id, authors.name as author_name, feedback_agg.overall_score as overall_score FROM ( SELECT talk_id, avg(score) as overall_score FROM feedbacks WHERE feedbacks.comment NOT IN ('', 'NA', 'N/A', 'not applicable') GROUP BY talk_id ) as feedback_agg INNER JOIN talks ON feedback_agg.talk_id = talks.id INNER JOIN authors ON talks.author_id = authors.id INNER JOIN clubs ON talks.club_id = clubs.id INNER JOIN cities ON clubs.city_id = cities.id INNER JOIN technologies ON clubs.technology_id = technologies.id; CREATE INDEX ON mv_talks_report (overall_score); SQL end def down connection.execute 'DROP MATERIALIZED VIEW IF EXISTS mv_talks_report' end end
  • 19. ActiveRecord Model Change class TalkReport < ActiveRecord::Base # No changes to associations belongs_to … self.table_name = 'mv_talks_report' def self.repopulate connection.execute("REFRESH MATERIALIZED VIEW #{table_name}") end # Materialized Views cannot be modified def readonly true end end
  • 20. Highest Scoring Talks 99% reduction in runtime 577ms Feedback.filled_out .select('talk_id, avg(score) as score') .group('talk_id').order('score desc').limit(10) 1ms TalkReport.order(overall_score: :desc).limit(10)
  • 21. Highest Scoring Talks in PA by Authors named “Parker” 95% reduction in runtime 400ms Feedback.filled_out.joins(talk: [:author, { club: :city }] ) .select('feedbacks.talk_id, avg(feedbacks.score) as overall_score') .where("cities.state_abbr = ?", 'PA') .where("authors.name LIKE '%?%'", 'Parker') .group('feedbacks.talk_id') .order('overall_score desc') .limit(10) 19ms TalkReport.where(state_abbr: 'PA') .where("author_name LIKE '%?%'", 'Parker') .order(overall_score: :desc).limit(10)
  • 22. Why Use Materialized Views in your Rails application? • ActiveRecord models allow for easy representation in Rails • Capture commonly used joins / filters • Allows for fast, live filtering and sorting of complex associations or calculated fields • Push data intensive processing out of Ruby to Database • Make use of advanced Database functions • Can Optimize Indexes for reporting only • When Performance is more important than Storage
  • 23. Downsides • Requires PostgreSQL 9.3 • Entire Materialized View must be refreshed to update • Bad when Live Data is required • For this use case, roll your own Materialized View using standard tables
  • 24. Downsides • Migrations are painful! • Recommend writing in SQL, so no using scopes • Entire Materialized View must be dropped and redefined for any changes to the View or referring tables • Hard to read and track what changed
  • 25. Resources • Source Code used in talk • https://github.com/droberts84/materialized-view- demo • PostgreSQL Documentation • https://wiki.postgresql.org/wiki/ Materialized_Views