Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add query_array #10

Merged
merged 3 commits into from Jan 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Expand Up @@ -37,6 +37,12 @@ p conn.query_single('select 1 union select 2')

p conn.query_hash('select 1 as a, 2 as b union select 3, 4')
# [{"a" => 1, "b"=> 1},{"a" => 3, "b" => 4}

p conn.query_array("select 1 as a, '2' as b union select 3, 'e'")
# [[1, '2'], [3, 'e']]

p conn.query_array("select 1 as a, '2' as b union select 3, 'e'").to_h
# {1 => '2', 3 => 'e'}
```

## The query builder
Expand All @@ -63,9 +69,18 @@ end
The builder allows for `order_by`, `where`, `select`, `set`, `limit`, `join`, `left_join` and `offset`.

## Is it fast?

Yes, it is very fast. See benchmarks in [the bench directory](https://github.com/discourse/mini_sql/tree/master/bench).

**Comparison mini_sql methods**
```
query_array 1351.6 i/s
query 963.8 i/s - 1.40x slower
query_hash 787.4 i/s - 1.72x slower

query_single('select id from topics limit 1000') 2368.9 i/s
query_array('select id from topics limit 1000').flatten 1350.1 i/s - 1.75x slower
```

As a rule it will outperform similar naive PG code while remaining safe.

```ruby
Expand Down
82 changes: 81 additions & 1 deletion bench/topic_perf.rb
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'bundler/inline'

gemfile do
Expand Down Expand Up @@ -194,7 +196,7 @@ def mini_sql_title_id_query_single
$swift = Swift::DB::Postgres.new(db: "test_db")

def swift_select_title_id(l=1000)
s = ""
s = +''
i = 0
r = $swift.execute("select id, title from topics order by id limit 1000")
while i < r.selected_rows
Expand All @@ -219,6 +221,84 @@ def swift_select_title_id(l=1000)
exit(-1) unless results.uniq.length == 1


#Benchmark.ips do |r|
# r.report('string') do |n|
# while n > 0
# s = +''
# 1_000.times { |i| s << i; s << i }
# n -= 1
# end
# end
# r.report('array') do |n|
# while n > 0
# 1_000.times { |i| [i, i] }
# n -= 1
# end
# end
#
# r.compare!
#end

# Comparison:
# array: 13041.2 i/s
# string: 4254.9 i/s - 3.06x slower
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in next PR I suggest replaced code

  s = +""
  Topic.limit(1000).order(:id).pluck(:id, :title).each do |id, title|
    s << id.to_s
    s << title
  end
  s

to

  Topic.limit(1000).order(:id).pluck(:id, :title).each do |id, title|
    [id, title]
  end

because use string add unnecessary overhead into benchmarks

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep in mind, the benchmark is meant to create the strings, the idea behind it is "What is the fastest way of generating the same exact string from multiple libraries.


Benchmark.ips do |r|
r.report('query_hash') do |n|
while n > 0
$mini_sql.query_hash('select id, title from topics order by id limit 1000').each do |hash|
[hash['id'], hash['title']]
end
n -= 1
end
end
r.report('query_array') do |n|
while n > 0
$mini_sql.query_array('select id, title from topics order by id limit 1000').each do |id, title|
[id, title]
end
n -= 1
end
end
r.report('query') do |n|
while n > 0
$mini_sql.query('select id, title from topics order by id limit 1000').each do |obj|
[obj.id, obj.title]
end
n -= 1
end
end

r.compare!
end

# Comparison:
# query_array: 1351.6 i/s
# query: 963.8 i/s - 1.40x slower
# query_hash: 787.4 i/s - 1.72x slower


Benchmark.ips do |r|
r.report('query_single') do |n|
while n > 0
$mini_sql.query_single('select id from topics order by id limit 1000')
n -= 1
end
end
r.report('query_array') do |n|
while n > 0
$mini_sql.query_array('select id from topics order by id limit 1000').flatten
n -= 1
end
end

r.compare!
end

# Comparison:
# query_single: 2368.9 i/s
# query_array: 1350.1 i/s - 1.75x slower

Benchmark.ips do |r|
r.report("ar select title id") do |n|
while n > 0
Expand Down
4 changes: 4 additions & 0 deletions lib/mini_sql/mysql/connection.rb
Expand Up @@ -20,6 +20,10 @@ def query_hash(sql, *params)
result.to_a
end

def query_array(sql, *params)
run(sql, :array, params).to_a
end

def exec(sql, *params)
run(sql, :array, params)
raw_connection.affected_rows
Expand Down
8 changes: 8 additions & 0 deletions lib/mini_sql/postgres/connection.rb
Expand Up @@ -78,6 +78,14 @@ def query_single(sql, *params)
result.clear if result
end

def query_array(sql, *params)
result = run(sql, params)
result.type_map = type_map
result.values
ensure
result.clear if result
end

def query(sql, *params)
result = run(sql, params)
result.type_map = type_map
Expand Down
4 changes: 4 additions & 0 deletions lib/mini_sql/sqlite/connection.rb
Expand Up @@ -26,6 +26,10 @@ def query_hash(sql, *params)
r
end

def query_array(sql, *params)
run(sql, *params)
end

def exec(sql, *params)

start = raw_connection.total_changes
Expand Down
5 changes: 5 additions & 0 deletions test/mini_sql/connection_tests.rb
Expand Up @@ -76,6 +76,11 @@ def test_query_hash
assert_equal([{ "a" => 1, "b" => '2' }, { "a" => 3, "b" => "e" }], r)
end

def test_query_array
r = @connection.query_array("select 1 as a, '2' as b union select 3, 'e'")
assert_equal([[1, '2'], [3, 'e']], r)
end

def test_too_many_params_hash
r = @connection.query_single("select 100", {a: 99})
assert_equal(r[0], 100)
Expand Down