How to query with string variable in rails 3.2.12? - sql

We would like to store both rails query string and table name in db and retrieve them for execution at run time. Here is the scenario:
Retrieve active customer records from customers table. Let's say we have 2 variable defined as:
table_name = 'Customer'
query_string = ':active => true'
In rails, the query could be:
records = Customer.where(:active => true)
Now with table name and query string stored in variables table_name and query_string, is it possible to assemble a query string with 2 variables like:
records = table_name.where(query_string) ?
Thanks for the help.

You could do this, but it's not generally recommended to evaluate a string as a hash. Also, table_name is an unfortunate name for the variable, because you actually are storing the class name (table would be 'customers'). In any event, what you are missing is the eval of these strings:
records = class_name.constantize.where(instance_eval(query_string))
Note that running instance_eval on a user-inputted string can be disasterous for security and the well-being of your application. Use with care, and stick to building an actual hash.

The definition of instance_eval is: Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj).
Another way to eval a query string is to include where in the string, like:
table_name = 'Customer'
query_string = 'where(:active => true)'
Then the record could be retrieved by:
records = table_name.constantize.instance_eval(query_string)
By putting where into the string, we can use the full power of instance_eval instead of just returning the source code to where as in the question above.

Related

Select Statement in SQLite Python - Using Variables in WHERE clause

Say I have a class variable restemail which stores the email id I need to use to sort out from the select statement in SQLite (Python). Whenever I refer to that variable after my WHERE clause, SQLite treats it as a column and returns an error saying that such a column doesn't exist. Something like this:
restemail=StringVar()
Password=StringVar()
def database(self):
conn = sqlite3.connect('data.db')
with conn:
cursor=conn.cursor()
strrest = self.restemail
cursor.execute('SELECT * FROM Restaurant3 WHERE restemail = strrest')
Can someone tell me how to use a variable inside my SQL queries without it being treated as a column name?
Any help will be appreciated.
Try the sqlite3 variable substitution syntax:
cursor.execute('SELECT * FROM Restaurant3 WHERE restemail = ?', (strrest,))

using .where with .find

I am wondering how I can use where cause with the ActiveRecord find method.
Here is the code I am using:
Supplier.joins(:products).find(params[:id]).where('suppliers.permalink = ? AND variants.master = ?', params[:id], TRUE)
which gives me:
undefined method `where' for #<Supplier:0x007fe49b4eb330>
Supplier.joins(:products).find(params[:id]).where('suppliers.permalink = ? AND variants.master = ?', params[:id], TRUE)
What you're doing here is finding the first record with the id contained in params[:id], then trying to run a where statement on that single record. where only works when run against the model itself.
The confusing part here is that you are using params[:id] both for the primary key (find searches the id field) but then also comparing it to the permalink column in the where clause.
To explain the usage of both methods:
find will search for result(s) from the table, matching the argument you provide it to the id field. You can pass in multiple id's and this method is mostly used to select a row that you know exists, by id. Most commonly it is used with a single id and returns a single instance.
where is used to find all results from the table that match the clause and return a collection of records. You can then refine these results or select one, for example by using .first:
Supplier.joins(:products).where('suppliers.permalink = ? AND variants.master = ?', params[:permalink], true).first
(Note that you're using joins(:products) but then querying variants table. Is this incorrect?)
Supplier.joins(:products).where('suppliers.permalink = ? AND variants.master = ?', params[:id], TRUE).find(params[:id])

Perl DBI - binding a list

How do I bind a variable to a SQL set for an IN query in Perl DBI?
Example:
my #nature = ('TYPE1','TYPE2'); # This is normally populated from elsewhere
my $qh = $dbh->prepare(
"SELECT count(ref_no) FROM fm_fault WHERE nature IN ?"
) || die("Failed to prepare query: $DBI::errstr");
# Using the array here only takes the first entry in this example, using a array ref gives no result
# bind_param and named bind variables gives similar results
$qh->execute(#nature) || die("Failed to execute query: $DBI::errstr");
print $qh->fetchrow_array();
The result for the code as above results in only the count for TYPE1, while the required output is the sum of the count for TYPE1 and TYPE2. Replacing the bind entry with a reference to #nature (\#nature), results in 0 results.
The main use-case for this is to allow a user to check multiple options using something like a checkbox group and it is to return all the results. A work-around is to construct a string to insert into the query - it works, however it needs a whole lot of filtering to avoid SQL injection issues and it is ugly...
In my case, the database is Oracle, ideally I want a generic solution that isn't affected by the database.
There should be as many ? placeholders as there is elements in #nature, ie. in (?,?,..)
my #nature = ('TYPE1','TYPE2');
my $pholders = join ",", ("?") x #nature;
my $qh = $dbh->prepare(
"SELECT count(ref_no) FROM fm_fault WHERE nature IN ($pholders)"
) or die("Failed to prepare query: $DBI::errstr");

multiple parameter "IN" prepared statement

I was trying to figure out how can I set multiple parameters for the IN clause in my SQL query using PreparedStatement.
For example in this SQL statement, I'll be having indefinite number of ?.
select * from ifs_db where img_hub = ? and country IN (multiple ?)
I've read about this in
PreparedStatement IN clause alternatives?
However I can't figure it out how to apply it to my SQL statement above.
There's not a standard way to handle this.
In SQL Server, you can use a table-valued parameter in a stored procedure and pass the countries in a table and use it in a join.
I've also seen cases where a comma-separated list is passed in and then parsed into a table by a function and then used in a join.
If your countries are standard ISO codes in a delimited list like '#US#UK#DE#NL#', you can use a rather simplistic construct like:
select * from ifs_db where img_hub = ? and ? LIKE '%#' + country + '#%'
Sormula will work for any data type (even custom types). This example uses int's for simplicity.
ArrayList<Integer> partNumbers = new ArrayList<Integer>();
partNumbers.add(999);
partNumbers.add(777);
partNumbers.add(1234);
// set up
Database database = new Database(getConnection());
Table<Inventory> inventoryTable = database.getTable(Inventory.class);
ArrayListSelectOperation<Inventory> operation =
new ArrayListSelectOperation<Inventory>(inventoryTable, "partNumberIn");
// show results
for (Inventory inventory: operation.selectAll(partNumbers))
System.out.println(inventory.getPartNumber());
You could use setArray method as mentioned in the javadoc below:
http://docs.oracle.com/javase/6/docs/api/java/sql/PreparedStatement.html#setArray(int, java.sql.Array)
Code:
PreparedStatement statement = connection.prepareStatement("Select * from test where field in (?)");
Array array = statement.getConnection().createArrayOf("VARCHAR", new Object[]{"AA1", "BB2","CC3"});
statement.setArray(1, array);
ResultSet rs = statement.executeQuery();

How to specify multiple values in where with AR query interface in rails3

Per section 2.2 of rails guide on Active Record query interface here:
which seems to indicate that I can pass a string specifying the condition(s), then an array of values that should be substituted at some point while the arel is being built. So I've got a statement that generates my conditions string, which can be a varying number of attributes chained together with either AND or OR between them, and I pass in an array as the second arg to the where method, and I get:
ActiveRecord::PreparedStatementInvalid: wrong number of bind variables (1 for 5)
which leads me to believe I'm doing this incorrectly. However, I'm not finding anything on how to do it correctly. To restate the problem another way, I need to pass in a string to the where method such as "table.attribute = ? AND table.attribute1 = ? OR table.attribute1 = ?" with an unknown number of these conditions anded or ored together, and then pass something, what I thought would be an array as the second argument that would be used to substitute the values in the first argument conditions string. Is this the correct approach, or, I'm just missing some other huge concept somewhere and I'm coming at this all wrong? I'd think that somehow, this has to be possible, short of just generating a raw sql string.
This is actually pretty simple:
Model.where(attribute: [value1,value2])
Sounds like you're doing something like this:
Model.where("attribute = ? OR attribute2 = ?", [value, value])
Whereas you need to do this:
# notice the lack of an array as the last argument
Model.where("attribute = ? OR attribute2 = ?", value, value)
Have a look at http://guides.rubyonrails.org/active_record_querying.html#array-conditions for more details on how this works.
Instead of passing the same parameter multiple times to where() like this
User.where(
"first_name like ? or last_name like ? or city like ?",
"%#{search}%", "%#{search}%", "%#{search}%"
)
you can easily provide a hash
User.where(
"first_name like :search or last_name like :search or city like :search",
{search: "%#{search}%"}
)
that makes your query much more readable for long argument lists.
Sounds like you're doing something like this:
Model.where("attribute = ? OR attribute2 = ?", [value, value])
Whereas you need to do this:
#notice the lack of an array as the last argument
Model.where("attribute = ? OR attribute2 = ?", value, value) Have a
look at
http://guides.rubyonrails.org/active_record_querying.html#array-conditions
for more details on how this works.
Was really close. You can turn an array into a list of arguments with *my_list.
Model.where("id = ? OR id = ?", *["1", "2"])
OR
params = ["1", "2"]
Model.where("id = ? OR id = ?", *params)
Should work
If you want to chain together an open-ended list of conditions (attribute names and values), I would suggest using an arel table.
It's a bit hard to give specifics since your question is so vague, so I'll just explain how to do this for a simple case of a Post model and a few attributes, say title, summary, and user_id (i.e. a user has_many posts).
First, get the arel table for the model:
table = Post.arel_table
Then, start building your predicate (which you will eventually use to create an SQL query):
relation = table[:title].eq("Foo")
relation = relation.or(table[:summary].eq("A post about foo"))
relation = relation.and(table[:user_id].eq(5))
Here, table[:title], table[:summary] and table[:user_id] are representations of columns in the posts table. When you call table[:title].eq("Foo"), you are creating a predicate, roughly equivalent to a find condition (get all rows whose title column equals "Foo"). These predicates can be chained together with and and or.
When your aggregate predicate is ready, you can get the result with:
Post.where(relation)
which will generate the SQL:
SELECT "posts".* FROM "posts"
WHERE (("posts"."title" = "Foo" OR "posts"."summary" = "A post about foo")
AND "posts"."user_id" = 5)
This will get you all posts that have either the title "Foo" or the summary "A post about foo", and which belong to a user with id 5.
Notice the way arel predicates can be endlessly chained together to create more and more complex queries. This means that if you have (say) a hash of attribute/value pairs, and some way of knowing whether to use AND or OR on each of them, you can loop through them one by one and build up your condition:
relation = table[:title].eq("Foo")
hash.each do |attr, value|
relation = relation.and(table[attr].eq(value))
# or relation = relation.or(table[attr].eq(value)) for an OR predicate
end
Post.where(relation)
Aside from the ease of chaining conditions, another advantage of arel tables is that they are independent of database, so you don't have to worry whether your MySQL query will work in PostgreSQL, etc.
Here's a Railscast with more on arel: http://railscasts.com/episodes/215-advanced-queries-in-rails-3?view=asciicast
Hope that helps.
You can use a hash rather than a string. Build up a hash with however many conditions and corresponding values you are going to have and put it into the first argument of the where method.
WRONG
This is what I used to do for some reason.
keys = params[:search].split(',').map!(&:downcase)
# keys are now ['brooklyn', 'queens']
query = 'lower(city) LIKE ?'
if keys.size > 1
# I need something like this depending on number of keys
# 'lower(city) LIKE ? OR lower(city) LIKE ? OR lower(city) LIKE ?'
query_array = []
keys.size.times { query_array << query }
#['lower(city) LIKE ?','lower(city) LIKE ?']
query = query_array.join(' OR ')
# which gives me 'lower(city) LIKE ? OR lower(city) LIKE ?'
end
# now I can query my model
# if keys size is one then keys are just 'brooklyn',
# in this case it is 'brooklyn', 'queens'
# #posts = Post.where('lower(city) LIKE ? OR lower(city) LIKE ?','brooklyn', 'queens' )
#posts = Post.where(query, *keys )
now however - yes - it's very simple. as nfriend21 mentioned
Model.where(attribute: [value1,value2])
does the same thing