mini_sql 0.2.2 → 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +66 -0
- data/.rubocop-https---raw-githubusercontent-com-discourse-discourse-master--rubocop-yml +355 -0
- data/.rubocop.yml +8 -0
- data/CHANGELOG +22 -0
- data/Gemfile +3 -1
- data/Guardfile +2 -0
- data/README.md +90 -2
- data/Rakefile +3 -1
- data/bench/timestamp_perf.rb +22 -21
- data/bench/topic_mysql_perf.rb +304 -0
- data/bench/topic_perf.rb +175 -11
- data/bin/console +1 -0
- data/lib/mini_sql.rb +8 -0
- data/lib/mini_sql/builder.rb +10 -2
- data/lib/mini_sql/connection.rb +17 -3
- data/lib/mini_sql/decoratable.rb +22 -0
- data/lib/mini_sql/deserializer_cache.rb +2 -0
- data/lib/mini_sql/inline_param_encoder.rb +9 -9
- data/lib/mini_sql/mysql/connection.rb +67 -0
- data/lib/mini_sql/mysql/deserializer_cache.rb +57 -0
- data/lib/mini_sql/postgres/coders.rb +2 -0
- data/lib/mini_sql/postgres/connection.rb +80 -3
- data/lib/mini_sql/postgres/deserializer_cache.rb +33 -13
- data/lib/mini_sql/postgres_jdbc/connection.rb +6 -1
- data/lib/mini_sql/postgres_jdbc/deserializer_cache.rb +43 -43
- data/lib/mini_sql/result.rb +20 -0
- data/lib/mini_sql/serializer.rb +70 -0
- data/lib/mini_sql/sqlite/connection.rb +12 -4
- data/lib/mini_sql/sqlite/deserializer_cache.rb +11 -13
- data/lib/mini_sql/version.rb +2 -1
- data/mini_sql.gemspec +15 -3
- metadata +80 -13
- data/.travis.yml +0 -21
data/Gemfile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source "https://rubygems.org"
|
2
4
|
|
3
|
-
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
5
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
4
6
|
|
5
7
|
# Specify your gem's dependencies in mini_sql.gemspec
|
6
8
|
gemspec
|
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -32,11 +32,28 @@ conn.query("select 1 id, 'bob' name").each do |user|
|
|
32
32
|
puts user.id # 1
|
33
33
|
end
|
34
34
|
|
35
|
+
# extend result objects with additional method
|
36
|
+
module ProductDecorator
|
37
|
+
def amount_price
|
38
|
+
price * quantity
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
conn.query_decorator(ProductDecorator, "select 20 price, 3 quantity").each do |user|
|
43
|
+
puts user.amount_price # 60
|
44
|
+
end
|
45
|
+
|
35
46
|
p conn.query_single('select 1 union select 2')
|
36
47
|
# [1,2]
|
37
48
|
|
38
49
|
p conn.query_hash('select 1 as a, 2 as b union select 3, 4')
|
39
50
|
# [{"a" => 1, "b"=> 1},{"a" => 3, "b" => 4}
|
51
|
+
|
52
|
+
p conn.query_array("select 1 as a, '2' as b union select 3, 'e'")
|
53
|
+
# [[1, '2'], [3, 'e']]
|
54
|
+
|
55
|
+
p conn.query_array("select 1 as a, '2' as b union select 3, 'e'").to_h
|
56
|
+
# {1 => '2', 3 => 'e'}
|
40
57
|
```
|
41
58
|
|
42
59
|
## The query builder
|
@@ -63,9 +80,18 @@ end
|
|
63
80
|
The builder allows for `order_by`, `where`, `select`, `set`, `limit`, `join`, `left_join` and `offset`.
|
64
81
|
|
65
82
|
## Is it fast?
|
66
|
-
|
67
83
|
Yes, it is very fast. See benchmarks in [the bench directory](https://github.com/discourse/mini_sql/tree/master/bench).
|
68
84
|
|
85
|
+
**Comparison mini_sql methods**
|
86
|
+
```
|
87
|
+
query_array 1351.6 i/s
|
88
|
+
query 963.8 i/s - 1.40x slower
|
89
|
+
query_hash 787.4 i/s - 1.72x slower
|
90
|
+
|
91
|
+
query_single('select id from topics limit 1000') 2368.9 i/s
|
92
|
+
query_array('select id from topics limit 1000').flatten 1350.1 i/s - 1.75x slower
|
93
|
+
```
|
94
|
+
|
69
95
|
As a rule it will outperform similar naive PG code while remaining safe.
|
70
96
|
|
71
97
|
```ruby
|
@@ -98,6 +124,68 @@ MiniSql is careful to always clear results as soon as possible.
|
|
98
124
|
|
99
125
|
MiniSql's default type mapper prefers treating `timestamp without time zone` columns as utc. This is done to ensure widest amount of compatability and is a departure from the default in the PG 1.0 gem. If you wish to amend behavior feel free to pass in a custom type_map.
|
100
126
|
|
127
|
+
## Custom type maps
|
128
|
+
|
129
|
+
When using Postgres, native type mapping implementation is used. This is roughly
|
130
|
+
implemented as:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
type_map ||= PG::BasicTypeMapForResults.new(conn)
|
134
|
+
# additional specific decoders
|
135
|
+
```
|
136
|
+
|
137
|
+
The type mapper instansitated once on-demand at boot and reused by all mini_sql connections.
|
138
|
+
|
139
|
+
Initializing the basic type map for Postgres can be a costly operation. You may
|
140
|
+
wish to amend the type mapper so for example you only return strings:
|
141
|
+
|
142
|
+
```
|
143
|
+
# maybe you do not want Integer
|
144
|
+
p cnn.query("select a 1").first.a
|
145
|
+
"1"
|
146
|
+
```
|
147
|
+
|
148
|
+
To specify a different type mapper for your results use:
|
149
|
+
|
150
|
+
```
|
151
|
+
MiniSql::Connections.get(pg_connection, type_map: custom_type_map)
|
152
|
+
```
|
153
|
+
|
154
|
+
In the case of Rails you can opt to use the type mapper Rails uses with:
|
155
|
+
|
156
|
+
```
|
157
|
+
pg_cnn = ActiveRecord::Base.connection.raw_connection
|
158
|
+
mini_sql_cnn = MiniSql::Connection.get(pg_cnn, type_map: pg_cnn.type_map_for_results)
|
159
|
+
```
|
160
|
+
|
161
|
+
Note the type mapper for Rails may miss some of the mapping MiniSql ships with such as `IPAddr`, MiniSql is also careful to use the very efficient TimestampUtc decoders where available.
|
162
|
+
|
163
|
+
## Streaming support
|
164
|
+
|
165
|
+
In some exceptional cases you may want to stream results directly from the database. This enables selection of 100s of thousands of rows with limited memory impact.
|
166
|
+
|
167
|
+
Two interfaces exists for this:
|
168
|
+
|
169
|
+
`query_each` : which can be used to get materialized objects
|
170
|
+
`query_each_hash` : which can be used to iterate through Hash objects
|
171
|
+
|
172
|
+
Usage:
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
mini_sql_cnn.query_each("SELECT * FROM tons_of_cows limit :limit", limit: 1_000_000) do |row|
|
176
|
+
puts row.cow_name
|
177
|
+
puts row.cow_age
|
178
|
+
end
|
179
|
+
|
180
|
+
mini_sql_cnn.query_each_hash("SELECT * FROM one_million_cows") do |row|
|
181
|
+
puts row["cow_name"]
|
182
|
+
puts row["cow_age"]
|
183
|
+
end
|
184
|
+
```
|
185
|
+
|
186
|
+
Note, in Postgres streaming is going to be slower than non-streaming options due to internal implementation in the pq gem, each row gets a full result object and additional bookkeeping is needed. Only use it if you need to optimize memory usage.
|
187
|
+
|
188
|
+
Streaming support is only implemented in the postgres backend at the moment, PRs welcome to add to other backends.
|
101
189
|
|
102
190
|
## I want more features!
|
103
191
|
|
@@ -109,7 +197,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
109
197
|
|
110
198
|
## Contributing
|
111
199
|
|
112
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
200
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/discourse/mini_sql. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
113
201
|
|
114
202
|
## License
|
115
203
|
|
data/Rakefile
CHANGED
data/bench/timestamp_perf.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler/inline'
|
2
4
|
|
3
5
|
gemfile do
|
@@ -21,8 +23,8 @@ require 'benchmark/ips'
|
|
21
23
|
require 'mini_sql'
|
22
24
|
|
23
25
|
ActiveRecord::Base.establish_connection(
|
24
|
-
:
|
25
|
-
:
|
26
|
+
adapter: "postgresql",
|
27
|
+
database: "test_db"
|
26
28
|
)
|
27
29
|
|
28
30
|
Sequel.default_timezone = :utc
|
@@ -47,20 +49,20 @@ SQL
|
|
47
49
|
class Timestamp < ActiveRecord::Base
|
48
50
|
end
|
49
51
|
|
50
|
-
class TimestampSequel< Sequel::Model(:timestamps)
|
52
|
+
class TimestampSequel < Sequel::Model(:timestamps)
|
51
53
|
end
|
52
54
|
|
53
|
-
|
54
55
|
Timestamp.transaction do
|
55
56
|
stamps = {
|
56
57
|
}
|
57
58
|
Timestamp.columns.each do |c|
|
58
|
-
stamps[c.name.to_sym] =
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
stamps[c.name.to_sym] =
|
60
|
+
case c.type
|
61
|
+
when :integer then 1
|
62
|
+
when :datetime then Time.now
|
63
|
+
when :boolean then false
|
64
|
+
else "HELLO WORLD" * 2
|
65
|
+
end
|
64
66
|
end
|
65
67
|
|
66
68
|
1000.times do |id|
|
@@ -71,7 +73,7 @@ end
|
|
71
73
|
|
72
74
|
$conn = ActiveRecord::Base.connection.raw_connection
|
73
75
|
|
74
|
-
def ar_pluck_times(l=1000)
|
76
|
+
def ar_pluck_times(l = 1000)
|
75
77
|
s = +""
|
76
78
|
Timestamp.limit(l).order(:id).pluck(:time1, :time2).each do |time1, time2|
|
77
79
|
s << time1.to_f.to_s
|
@@ -80,7 +82,7 @@ def ar_pluck_times(l=1000)
|
|
80
82
|
s
|
81
83
|
end
|
82
84
|
|
83
|
-
def ar_select_times(l=1000)
|
85
|
+
def ar_select_times(l = 1000)
|
84
86
|
s = +""
|
85
87
|
Timestamp.limit(l).order(:id).select(:time1, :time2).each do |t|
|
86
88
|
s << t.time1.to_f.to_s
|
@@ -91,7 +93,7 @@ end
|
|
91
93
|
|
92
94
|
$mini_sql = MiniSql::Connection.new($conn)
|
93
95
|
|
94
|
-
def pg_times_params(l=1000)
|
96
|
+
def pg_times_params(l = 1000)
|
95
97
|
s = +""
|
96
98
|
# use the safe pattern here
|
97
99
|
r = $conn.async_exec_params(-"select time1, time2 from timestamps order by id limit $1", [l])
|
@@ -110,7 +112,7 @@ def pg_times_params(l=1000)
|
|
110
112
|
s
|
111
113
|
end
|
112
114
|
|
113
|
-
def pg_times(l=1000)
|
115
|
+
def pg_times(l = 1000)
|
114
116
|
s = +""
|
115
117
|
# use the safe pattern here
|
116
118
|
r = $conn.async_exec("select time1, time2 from timestamps order by id limit #{l}")
|
@@ -129,7 +131,7 @@ def pg_times(l=1000)
|
|
129
131
|
s
|
130
132
|
end
|
131
133
|
|
132
|
-
def mini_sql_times(l=1000)
|
134
|
+
def mini_sql_times(l = 1000)
|
133
135
|
s = +""
|
134
136
|
$mini_sql.query(-"select time1, time2 from timestamps order by id limit ?", l).each do |t|
|
135
137
|
s << t.time1.to_f.to_s
|
@@ -138,7 +140,7 @@ def mini_sql_times(l=1000)
|
|
138
140
|
s
|
139
141
|
end
|
140
142
|
|
141
|
-
def sequel_times(l=1000)
|
143
|
+
def sequel_times(l = 1000)
|
142
144
|
s = +""
|
143
145
|
TimestampSequel.limit(l).order(:id).select(:time1, :time2).each do |t|
|
144
146
|
s << t.time1.to_f.to_s
|
@@ -147,7 +149,7 @@ def sequel_times(l=1000)
|
|
147
149
|
s
|
148
150
|
end
|
149
151
|
|
150
|
-
def sequel_pluck_times(l=1000)
|
152
|
+
def sequel_pluck_times(l = 1000)
|
151
153
|
s = +""
|
152
154
|
TimestampSequel.limit(l).order(:id).select_map([:time1, :time2]).each do |t|
|
153
155
|
s << t[0].to_f.to_s
|
@@ -156,7 +158,7 @@ def sequel_pluck_times(l=1000)
|
|
156
158
|
s
|
157
159
|
end
|
158
160
|
|
159
|
-
def sequel_raw_times(l=1000)
|
161
|
+
def sequel_raw_times(l = 1000)
|
160
162
|
s = +""
|
161
163
|
DB[-"select time1, time2 from timestamps order by id limit ?", l].map([:time1, :time2]).each do |time1, time2|
|
162
164
|
s << time1.to_f.to_s
|
@@ -166,13 +168,13 @@ def sequel_raw_times(l=1000)
|
|
166
168
|
end
|
167
169
|
|
168
170
|
# usage is not really recommended but just to compare to pluck lets have it
|
169
|
-
def mini_sql_times_single(l=1000)
|
171
|
+
def mini_sql_times_single(l = 1000)
|
170
172
|
s = +""
|
171
173
|
i = 0
|
172
174
|
r = $mini_sql.query_single(-"select time1, time2 from timestamps order by id limit ?", l)
|
173
175
|
while i < r.length
|
174
176
|
s << r[i].to_f.to_s
|
175
|
-
s << r[i+1].to_f.to_s
|
177
|
+
s << r[i + 1].to_f.to_s
|
176
178
|
i += 2
|
177
179
|
end
|
178
180
|
s
|
@@ -190,7 +192,6 @@ end
|
|
190
192
|
# s
|
191
193
|
# end
|
192
194
|
|
193
|
-
|
194
195
|
results = [
|
195
196
|
ar_select_times,
|
196
197
|
ar_pluck_times,
|
@@ -0,0 +1,304 @@
|
|
1
|
+
require 'bundler/inline'
|
2
|
+
|
3
|
+
gemfile do
|
4
|
+
source 'https://rubygems.org'
|
5
|
+
gem 'mysql2'
|
6
|
+
gem 'mini_sql', path: '../'
|
7
|
+
gem 'activesupport'
|
8
|
+
gem 'activerecord'
|
9
|
+
gem 'activemodel'
|
10
|
+
gem 'memory_profiler'
|
11
|
+
gem 'benchmark-ips'
|
12
|
+
gem 'sequel', github: 'jeremyevans/sequel'
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'mysql2'
|
16
|
+
require 'sequel'
|
17
|
+
require 'active_record'
|
18
|
+
require 'memory_profiler'
|
19
|
+
require 'benchmark/ips'
|
20
|
+
require 'mini_sql'
|
21
|
+
|
22
|
+
ActiveRecord::Base.establish_connection(
|
23
|
+
:adapter => "mysql2",
|
24
|
+
:database => "test_db",
|
25
|
+
:username => "root",
|
26
|
+
:password => ''
|
27
|
+
)
|
28
|
+
|
29
|
+
DB = Sequel.connect("mysql2://root:@localhost/test_db")
|
30
|
+
|
31
|
+
mysql = ActiveRecord::Base.connection.raw_connection
|
32
|
+
|
33
|
+
mysql.query <<SQL
|
34
|
+
drop table if exists topics
|
35
|
+
SQL
|
36
|
+
|
37
|
+
mysql.query <<~SQL
|
38
|
+
CREATE TABLE `topics` (
|
39
|
+
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
40
|
+
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
41
|
+
`last_posted_at` datetime DEFAULT NULL,
|
42
|
+
`created_at` datetime NOT NULL,
|
43
|
+
`updated_at` datetime NOT NULL,
|
44
|
+
`views` int(11) NOT NULL DEFAULT '0',
|
45
|
+
`posts_count` int(11) NOT NULL DEFAULT '0',
|
46
|
+
`user_id` int(11) DEFAULT NULL,
|
47
|
+
`last_post_user_id` int(11) NOT NULL,
|
48
|
+
`reply_count` int(11) NOT NULL DEFAULT '0',
|
49
|
+
`featured_user1_id` int(11) DEFAULT NULL,
|
50
|
+
`featured_user2_id` int(11) DEFAULT NULL,
|
51
|
+
`featured_user3_id` int(11) DEFAULT NULL,
|
52
|
+
`avg_time` int(11) DEFAULT NULL,
|
53
|
+
`deleted_at` datetime DEFAULT NULL,
|
54
|
+
`highest_post_number` int(11) NOT NULL DEFAULT '0',
|
55
|
+
`image_url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
56
|
+
`like_count` int(11) NOT NULL DEFAULT '0',
|
57
|
+
`incoming_link_count` int(11) NOT NULL DEFAULT '0',
|
58
|
+
`category_id` int(11) DEFAULT NULL,
|
59
|
+
`visible` tinyint(1) NOT NULL DEFAULT '1',
|
60
|
+
`moderator_posts_count` int(11) NOT NULL DEFAULT '0',
|
61
|
+
`closed` tinyint(1) NOT NULL DEFAULT '0',
|
62
|
+
`archived` tinyint(1) NOT NULL DEFAULT '0',
|
63
|
+
`bumped_at` datetime NOT NULL,
|
64
|
+
`has_summary` tinyint(1) NOT NULL DEFAULT '0',
|
65
|
+
`archetype` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'regular',
|
66
|
+
`featured_user4_id` int(11) DEFAULT NULL,
|
67
|
+
`notify_moderators_count` int(11) NOT NULL DEFAULT '0',
|
68
|
+
`spam_count` int(11) NOT NULL DEFAULT '0',
|
69
|
+
`pinned_at` datetime DEFAULT NULL,
|
70
|
+
`score` float DEFAULT NULL,
|
71
|
+
`percent_rank` float NOT NULL DEFAULT '1',
|
72
|
+
`subtype` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
73
|
+
`slug` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
74
|
+
`deleted_by_id` int(11) DEFAULT NULL,
|
75
|
+
`participant_count` int(11) DEFAULT '1',
|
76
|
+
`word_count` int(11) DEFAULT NULL,
|
77
|
+
`excerpt` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
78
|
+
`pinned_globally` tinyint(1) NOT NULL DEFAULT '0',
|
79
|
+
`pinned_until` datetime DEFAULT NULL,
|
80
|
+
`fancy_title` varchar(400) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
81
|
+
`highest_staff_post_number` int(11) NOT NULL DEFAULT '0',
|
82
|
+
`featured_link` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
83
|
+
`reviewable_score` float NOT NULL DEFAULT '0',
|
84
|
+
PRIMARY KEY (`id`)
|
85
|
+
)
|
86
|
+
SQL
|
87
|
+
|
88
|
+
class Topic < ActiveRecord::Base
|
89
|
+
end
|
90
|
+
|
91
|
+
class TopicSequel < Sequel::Model(:topics)
|
92
|
+
end
|
93
|
+
|
94
|
+
Topic.transaction do
|
95
|
+
topic = {
|
96
|
+
}
|
97
|
+
Topic.columns.each do |c|
|
98
|
+
topic[c.name.to_sym] = case c.type
|
99
|
+
when :integer then 1
|
100
|
+
when :datetime then Time.now
|
101
|
+
when :boolean then false
|
102
|
+
when :float then 1.0
|
103
|
+
else "HELLO WORLD" * 2
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
1000.times do |id|
|
108
|
+
topic[:id] = id
|
109
|
+
Topic.create!(topic)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
$conn = ActiveRecord::Base.connection.raw_connection
|
114
|
+
|
115
|
+
def ar_title_id_pluck
|
116
|
+
s = +""
|
117
|
+
Topic.limit(1000).order(:id).pluck(:id, :title).each do |id, title|
|
118
|
+
s << id.to_s
|
119
|
+
s << title
|
120
|
+
end
|
121
|
+
s
|
122
|
+
end
|
123
|
+
|
124
|
+
def ar_title_id
|
125
|
+
s = +""
|
126
|
+
Topic.limit(1000).order(:id).select(:id, :title).each do |t|
|
127
|
+
s << t.id.to_s
|
128
|
+
s << t.title
|
129
|
+
end
|
130
|
+
s
|
131
|
+
end
|
132
|
+
|
133
|
+
def mysql_title_id
|
134
|
+
s = +""
|
135
|
+
# use the safe pattern here
|
136
|
+
r = $conn.query(-"select id, title from topics order by id limit 1000", as: :array)
|
137
|
+
|
138
|
+
r.each do |row|
|
139
|
+
s << row[0].to_s
|
140
|
+
s << row[1]
|
141
|
+
end
|
142
|
+
s
|
143
|
+
end
|
144
|
+
|
145
|
+
$mini_sql = MiniSql::Connection.get($conn)
|
146
|
+
|
147
|
+
def mini_sql_title_id
|
148
|
+
s = +""
|
149
|
+
$mini_sql.query(-"select id, title from topics order by id limit 1000").each do |t|
|
150
|
+
s << t.id.to_s
|
151
|
+
s << t.title
|
152
|
+
end
|
153
|
+
s
|
154
|
+
end
|
155
|
+
|
156
|
+
def sequel_select_title_id
|
157
|
+
s = +""
|
158
|
+
TopicSequel.limit(1000).order(:id).select(:id, :title).each do |t|
|
159
|
+
s << t.id.to_s
|
160
|
+
s << t.title
|
161
|
+
end
|
162
|
+
s
|
163
|
+
end
|
164
|
+
|
165
|
+
def sequel_pluck_title_id
|
166
|
+
s = +""
|
167
|
+
TopicSequel.limit(1000).order(:id).select_map([:id, :title]).each do |t|
|
168
|
+
s << t[0].to_s
|
169
|
+
s << t[1]
|
170
|
+
end
|
171
|
+
s
|
172
|
+
end
|
173
|
+
|
174
|
+
# usage is not really recommended but just to compare to pluck lets have it
|
175
|
+
def mini_sql_title_id_query_single
|
176
|
+
s = +""
|
177
|
+
i = 0
|
178
|
+
r = $mini_sql.query_single(-"select id, title from topics order by id limit 1000")
|
179
|
+
while i < r.length
|
180
|
+
s << r[i].to_s
|
181
|
+
s << r[i + 1]
|
182
|
+
i += 2
|
183
|
+
end
|
184
|
+
s
|
185
|
+
end
|
186
|
+
|
187
|
+
results = [
|
188
|
+
ar_title_id,
|
189
|
+
ar_title_id_pluck,
|
190
|
+
mysql_title_id,
|
191
|
+
mini_sql_title_id,
|
192
|
+
sequel_pluck_title_id,
|
193
|
+
sequel_select_title_id,
|
194
|
+
mini_sql_title_id_query_single
|
195
|
+
]
|
196
|
+
|
197
|
+
exit(-1) unless results.uniq.length == 1
|
198
|
+
|
199
|
+
Benchmark.ips do |r|
|
200
|
+
r.report("ar select title id") do |n|
|
201
|
+
while n > 0
|
202
|
+
ar_title_id
|
203
|
+
n -= 1
|
204
|
+
end
|
205
|
+
end
|
206
|
+
r.report("ar select title id pluck") do |n|
|
207
|
+
while n > 0
|
208
|
+
ar_title_id_pluck
|
209
|
+
n -= 1
|
210
|
+
end
|
211
|
+
end
|
212
|
+
r.report("sequel title id select") do |n|
|
213
|
+
while n > 0
|
214
|
+
sequel_select_title_id
|
215
|
+
n -= 1
|
216
|
+
end
|
217
|
+
end
|
218
|
+
r.report("mysql select title id") do |n|
|
219
|
+
while n > 0
|
220
|
+
mysql_title_id
|
221
|
+
n -= 1
|
222
|
+
end
|
223
|
+
end
|
224
|
+
r.report("mini_sql select title id") do |n|
|
225
|
+
while n > 0
|
226
|
+
mini_sql_title_id
|
227
|
+
n -= 1
|
228
|
+
end
|
229
|
+
end
|
230
|
+
r.report("sequel title id pluck") do |n|
|
231
|
+
while n > 0
|
232
|
+
sequel_pluck_title_id
|
233
|
+
n -= 1
|
234
|
+
end
|
235
|
+
end
|
236
|
+
r.report("mini_sql query_single title id") do |n|
|
237
|
+
while n > 0
|
238
|
+
mini_sql_title_id_query_single
|
239
|
+
n -= 1
|
240
|
+
end
|
241
|
+
end
|
242
|
+
r.compare!
|
243
|
+
end
|
244
|
+
|
245
|
+
def wide_topic_ar
|
246
|
+
Topic.first
|
247
|
+
end
|
248
|
+
|
249
|
+
def wide_topic_mysql
|
250
|
+
r = $conn.query("select * from topics limit 1", as: :hash)
|
251
|
+
row = r.first
|
252
|
+
row
|
253
|
+
end
|
254
|
+
|
255
|
+
def wide_topic_sequel
|
256
|
+
TopicSequel.first
|
257
|
+
end
|
258
|
+
|
259
|
+
def wide_topic_mini_sql
|
260
|
+
$conn.query("select * from topics limit 1").first
|
261
|
+
end
|
262
|
+
|
263
|
+
Benchmark.ips do |r|
|
264
|
+
r.report("wide topic ar") do |n|
|
265
|
+
while n > 0
|
266
|
+
wide_topic_ar
|
267
|
+
n -= 1
|
268
|
+
end
|
269
|
+
end
|
270
|
+
r.report("wide topic sequel") do |n|
|
271
|
+
while n > 0
|
272
|
+
wide_topic_sequel
|
273
|
+
n -= 1
|
274
|
+
end
|
275
|
+
end
|
276
|
+
r.report("wide topic mysql") do |n|
|
277
|
+
while n > 0
|
278
|
+
wide_topic_mysql
|
279
|
+
n -= 1
|
280
|
+
end
|
281
|
+
end
|
282
|
+
r.report("wide topic mini sql") do |n|
|
283
|
+
while n > 0
|
284
|
+
wide_topic_mini_sql
|
285
|
+
n -= 1
|
286
|
+
end
|
287
|
+
end
|
288
|
+
r.compare!
|
289
|
+
end
|
290
|
+
|
291
|
+
# Comparison:
|
292
|
+
# mysql select title id: 485.0 i/s
|
293
|
+
# mini_sql query_single title id: 447.2 i/s - same-ish: difference falls within error
|
294
|
+
# mini_sql select title id: 417.4 i/s - 1.16x slower
|
295
|
+
# sequel title id pluck: 370.2 i/s - 1.31x slower
|
296
|
+
# sequel title id select: 351.0 i/s - 1.38x slower
|
297
|
+
# ar select title id pluck: 317.1 i/s - 1.53x slower
|
298
|
+
# ar select title id: 102.3 i/s - 4.74x slower
|
299
|
+
|
300
|
+
# Comparison:
|
301
|
+
# wide topic mini sql: 6768.7 i/s
|
302
|
+
# wide topic mysql: 6063.9 i/s - same-ish: difference falls within error
|
303
|
+
# wide topic sequel: 4908.6 i/s - same-ish: difference falls within error
|
304
|
+
# wide topic ar: 2630.2 i/s - 2.57x slower
|