Ruby DBI - How to check if a recordset is empty - ruby

Using Ruby DBI, how can I check if a record set is empty? (without iterating through it or doing a count query)
sth = dbh.prepare("select * from things WHERE created_at > '#{start}'")
sth.execute

You could always just ask the result object:
res = sth.execute
res.num_rows
The operation will have to pull down all matching records, though, so if you only need a count, you might want to select that directly.
Also escape your SQL. You cannot just put arbitrary strings in there. This is better:
sth = dbh.prepare("select * from things WHERE created_at > '%s'" % sth.escape_string(start))

Related

How to give Sequel#execute a block on a simple select?

I'm executing a simple query vis Sequel like such:
result = db.execute("SELECT some_int_colum FROM some_system_table WHERE some_column = 'some_value';")
In a psql session, it returns the expected value but when run through the Sequel Postgres adapter it returns the number of resulting rows, not the value.
From the source (reference):
# Execute the given SQL with this connection. If a block is given,
# yield the results, otherwise, return the number of changed rows.
That clearly explains the why, but how is a block given to the execute method in this scenario?
Dataset#fetch (reference) is a more appropriate method to execute arbitrary sql and return a single or set of values. In the above example, where only a single return value is expected it would look something like this:
result = db.fetch("SELECT some_int_colum FROM some_system_table WHERE some_column = 'some_value'").all.first.values.first

Put the sample results in an array, a hash, etc using ruby and oci8

I use ruby-2.3 and oci-8 gem. I want to make the select query:
stm = "select * from DATASERVICEUSERS t where boss<>100 and loginad is not null"
res = CONN.exec(stm).fetch_hash do |row|
#do something with row
end
CONN.logoff
How can I query the result of the whole to put for example in an array or hash, instead of cycle pass through each record? I need just a collection of elements of the result of this request.
Oci-8 doesn't provice that. The .exec method produces a cursor that you you need to process like your code demonstrates. You can fill up an array with an array of fields or a hash.
Here an example for an array
records = []
conn.exec(sql) { |record| records << record}
# records: [["xxxx", "xxxx"], ["yyyy", "yyyy"], ..]
I know this is quite an old question but I've come across this problem. I'm not as well versed in ruby but oci8 2.2.7 actually provides fetch_hash
https://www.rubydoc.info/gems/ruby-oci8/OCI8/Cursor#fetch_hash-instance_method
here's an example from my use case:
records = []
dataCursor = #odb.exec(queryUUNRData)
while((data = dataCursor.fetch_hash) != nil)
records.push data
end
dataCursor.close
the resulting dataset already includes the column names as hash key

Groovy Sql rows

Hello I am trying to get rows using Groovy Sql connection but it returns me records as a List inside a List. The following:
Sql sql = new Sql(dataSource)
List<GroovyRowResult> row = sql.rows('select * from user where username=:userName and password=:password, [userName:'groovy',password:'123'])
returns the result as [[return record as map]]
Any one help me to figure out why the result is a List inside a List. How will I get it as a single level List using the rows method?
Your results are coming back as a list of maps, not a list of lists. Look at the ':' and ',' chars in the inner part. You can use standard groovy extraction of values from these.
In your case, it looks like you're using a primary key search, so will only return one result, so use firstRow in this case, so that you don't have to extract the single map result from the list.
See the documentation for the groovy Sql class for examples.
In the more general case where you are returning multiple rows, then your data probably looks like this:
[[username:"foo", password:"foopass"], [username:"bar", password:"barpass"]]
Assuming the line:
def results = sql.rows('select * from user')
You can then do things like spread operators:
assert results.username == ["foo", "bar"]
assert results.password == ["foopass", "barpass"]
or iterate over the results
results.each { println it.username }
==> foo
==> bar
or use any of the many Collection functions
println results.collect { "${it.username} -> ${it.password}" }
==> [ "foo -> foopass", "bar -> barpass" ]
I think your main issue was not recognising a single map entry in a list.
It doesn't return a List inside a List, it returns a List of Map with each map containing the columns selected from your select.
So if you want all of the usernames selected (as a List), you can just do:
def usernames = row.username
If you just want a single row, you can do:
GroovyRowResult row = sql.firstRow('select * from user where username=:userName and password=:password, [userName:'groovy',password:'123'])
And then this will effectively just be a map with each key being the field name selected, and each value being the value of the first row of each field

multiple where statements combined with OR in Activerecord and Ruby

I would like to do a query with activerecord (not rails) with multiple keywords that are contained in a field (so I have to use LIKE) but I don't know in advance how many keywords there will be.
My query looks like this, Word is my model.
query = ['word1','word2'] #could be more
puts "searching for #{query}"
qwords = Word.none
query.each do |qword|
puts qwords.where("word like ?", "%#{qword}%").to_sql
qwords = qwords.where("word like ?", "%#{qword}%")
end
Which gives nothing because the queries are added as AND but I need OR.
searching for ["word1", "word2"]
SELECT "words".* FROM "words" WHERE (word like '%word1%')
SELECT "words".* FROM "words" WHERE (word like '%word1%') AND (word like '%word2%')
#<ActiveRecord::Relation []>
I can't use Word.where(word: query) which uses the sql IN keyword because that only works for exact matches.
Is there a solution that doesn't involves concatenating the whole SQL that is needed ?
query = "word1 word2" #could be more
puts "searching for #{query}"
query_length = query.split.length #calculates number of words in query
Now you can put together the number of SQL queries you need regardless of the number of keywords in your query
Word.where([(['word LIKE ?'] * query_length).join(' OR ')] + (query.split.map {|query| "%#{query}%"}))
This should return
["word LIKE ? OR word LIKE ?", "%word1%", "%word2%"]
for your SQL search
Had forgotten about this question and found a solution myself afterward.
I now do the following. The problem was caused by using the resultset to do my next query on while like this it is on the whole recordset and the results are added.
#qwords = Word.none
$query.each do |qword|
#qwords += Word.where(word: qword)
end

How to use Sequel to select one field from database

I am using Sinatra and Sequel with PostgreSQL.
After authentication, I want to welcome the user by printing their name but I cannot get only the value of the user's name from the database, it comes out as a hash.
The query is:
current_user = DB[:users].select(:username).where('password = ?', password).first
and the resulting piece of data is:
Welcome, {:username=>"Rich"}
which looks rather weird, I would prefer it to read "Welcome, Rich".
What am I doing wrong here? I tried the same query without 'first" at the end and that does not work either.
You can either pull the (single) column you selected out of the Hash you are given:
current_user = DB[:users].select(:username).where('password=?', password).first[:username]
Or you can map your results to an array of usernames and pull the first:
# Using a hash in the filter method is simpler than SQL placeholders.
current_user = DB[:users].filter(password:password).select_map(:username).first
But the best way is to get only the user you care about, and then get the name:
# Using [] on a dataset returns the first row matching the criteria
current_user = DB[:users][password:password][:username]
Try Sequel::Dataset#get. Also, as Phrogz points out, Sequel::Dataset#where can take a hash (it will securely escape values to prevent injection attacks).
current_username = DB[:users].where(password: password).get(:username)
There's also Sequel::Dataset#where_single_value, which is optimized for this exact situation:
current_username = DB[:users].select(:username).where_single_value(password: password)

Resources