Right now I have only one condition in my Projects.paginate
Code is below
def list
#projects = Project.paginate(:page => params[:page], :per_page => 100, :order => (sort_column + ' ' + arrow), :conditions => ["description LIKE ?", "%#{query}%"])
I want to put another condition here but its is proving to be difficult. I'v tried
#projects = Project.paginate(:page => params[:page], :per_page => 100, :order => (sort_column + ' ' + arrow), :conditions => ["description OR name LIKE ?", "%#{query}%"])
but im getting a bind error from the SQL controller. Any ideas? I cant use the = sign either.
You need to have two bind variables in your conditions array:
qt = "%#{query}%"
#projects = Project.paginate(:conditions =>
["description LIKE ? OR name LIKE ?", qt, qt], ..)
Related
I am using Query Builder with multiple where clause. When I use this query,
$query1 = new \yii\db\Query();
$query1->select('*')
->from('assessment_score ca')
->where(['AND','ca.is_status' => 0, 'ca.assessment_type' => 'CONTINUOUS ASSESSMENT', 'ca.ca_type' => 'CONTINUOUS ASSESSMENT'])
->andFilterWhere(['ca.state_office_id' => $model->report_state_office_id])
->andFilterWhere(['ca.study_centre_id' => $model->report_study_centre_id])
->andFilterWhere(['ca.programme_id' => $model->report_programme_id])
->andFilterWhere(['ca.department_id' => $model->report_department_id])
->andFilterWhere(['ca.academic_level_id' => $model->report_academic_level_id])
->andFilterWhere(['ca.academic_year_id' => $model->report_academic_year_id])
->andFilterWhere(['ca.academic_semester_id' => $model->report_academic_semester_id])
->andFilterWhere(['ca.course_id' => $model->report_course_id]);
$command=$query1->createCommand();
$ca_data=$command->queryAll();
I got this error
Then, when I changed the code to this, no response:
$selected_list = $_POST['ca'];
$query1 = new \yii\db\Query();
$query1->select('*')
->from('assessment_score ca')
->where(['ca.is_status' => 0])
->andWhere(['ca.assessment_type' => 'CONTINUOUS ASSESSMENT'])
->andWhere(['ca.ca_type' => 'CONTINUOUS ASSESSMENT'])
->andFilterWhere(['ca.state_office_id' => $model->report_state_office_id])
->andFilterWhere(['ca.study_centre_id' => $model->report_study_centre_id])
->andFilterWhere(['ca.programme_id' => $model->report_programme_id])
->andFilterWhere(['ca.department_id' => $model->report_department_id])
->andFilterWhere(['ca.academic_level_id' => $model->report_academic_level_id])
->andFilterWhere(['ca.academic_year_id' => $model->report_academic_year_id])
->andFilterWhere(['ca.academic_semester_id' => $model->report_academic_semester_id])
->andFilterWhere(['ca.course_id' => $model->report_course_id]);
$command=$query1->createCommand();
$ca_data=$command->queryAll();
How do I re-write the code appropriately to solve the issue of multiple where clause?
You might need to change the query format for the where() statement as you need to provide every condition (name=>value pair) as a separate array rather than just name=>value pairs, you currently have
->where(['AND', 'ca.is_status' => 0, 'ca.assessment_type' => 'CONTINUOUS ASSESSMENT', 'ca.ca_type' => 'CONTINUOUS ASSESSMENT'])
which will create the query like below if no other parameter is provided for andFilterWhere() statements.
SELECT * FROM `assessment_score` `ca`
WHERE (0)
AND (CONTINUOUS ASSESSMENT) AND (CONTINUOUS ASSESSMENT)
which is incorrect and throwing the error, you can notice that in your Exception image, so change it to the one below
->where(['AND',
['ca.is_status' => 0],
['ca.assessment_type' => 'CONTINUOUS ASSESSMENT'],
['ca.ca_type' => 'CONTINUOUS ASSESSMENT']
])
which will output the query like
SELECT * FROM `assessment_score` `ca`
WHERE (`ca`.`is_status`=0)
AND (`ca`.`assessment_type`='CONTINUOUS ASSESSMENT')
AND (`ca`.`ca_type`='CONTINUOUS ASSESSMENT')
Your complete query should look like this
$query1 = new \yii\db\Query();
$query1->select('*')
->from('assessment_score ca')
->where(['AND',
['ca.is_status' => 0],
['ca.assessment_type' => 'CONTINUOUS ASSESSMENT'],
['ca.ca_type' => 'CONTINUOUS ASSESSMENT']
])
->andFilterWhere(['ca.state_office_id' => $model->report_state_office_id])
->andFilterWhere(['ca.study_centre_id' => $model->report_study_centre_id])
->andFilterWhere(['ca.programme_id' => $model->report_programme_id])
->andFilterWhere(['ca.department_id' => $model->report_department_id])
->andFilterWhere(['ca.academic_level_id' => $model->report_academic_level_id])
->andFilterWhere(['ca.academic_year_id' => $model->report_academic_year_id])
->andFilterWhere(['ca.academic_semester_id' => $model->report_academic_semester_id])
->andFilterWhere(['ca.course_id' => $model->report_course_id]);
$command = $query1->createCommand();
$ca_data = $command->queryAll();
based on yii2 guide for Operator Format
Operator format allows you to specify arbitrary conditions in a
programmatic way. It takes the following format:
[operator, operand1, operand2, ...] where the operands can each be
specified in string format, hash format or operator format
recursively, while the operator can be one of the following:
and: the operands should be concatenated together using AND. For
example, ['and', 'id=1', 'id=2']
so in your case should be
->where(['AND', 'ca.is_status = 0',
"ca.assessment_type = 'CONTINUOUS ASSESSMENT'",
"ca.ca_type = 'CONTINUOUS ASSESSMENT'"])
https://www.yiiframework.com/doc/guide/2.0/en/db-query-builder#operator-format
All you need is to remove AND from array passed to where():
->where([
'ca.is_status' => 0,
'ca.assessment_type' => 'CONTINUOUS ASSESSMENT',
'ca.ca_type' => 'CONTINUOUS ASSESSMENT'
])
If you pass associative array, it will be treated as pairs of column-value for conditions for WHERE in query. If you pass AND as first element, it is no longer a associative array, and query builder will ignore keys and only combine values as complete condition.
I want to create two indices for the same model and search separately
I am using
gem 'thinking-sphinx', '3.2.0'
gem 'riddle', '1.5.11'
ThinkingSphinx::Index.define :product, :with => :active_record, :delta => ThinkingSphinx::Deltas::DelayedDelta do
indexes :field_a
end
ThinkingSphinx::Index.define :product, :name => "active_product", :with => :active_record, :delta => ThinkingSphinx::Deltas::DelayedDelta do
indexes :field_a
where "(active = 1)"
end
when i tried to search this way to get only the active products
Product.search_for_ids "", :match_mode => :extended, :index => "active_product_core, active_product_delta", :page => params[:page], :per_page => 50, :sort_mode => :extended, :order => "field_a desc"
But it is running query like this and listing all products
SELECT * FROM `product_core`, `product_delta` WHERE `sphinx_deleted` = 0 ORDER BY `field_a` desc LIMIT 0, 50 OPTION max_matches=50000
How can i get only the active products or to make sure query runs like this?
SELECT * FROM `active_product_core`, `active_product_delta` WHERE `sphinx_deleted` = 0 ORDER BY `field_a` desc LIMIT 0, 50 OPTION max_matches=50000
Note: Above feature was working fine in Thinking sphinx version 2
gem 'thinking-sphinx', '2.0.14'
gem 'riddle', '1.5.3'
In TS v3, the search option is now :indices rather than :index, and expects an array of index names. So, try the following:
Product.search_for_ids(
:indices => ["active_product_core", "active_product_delta"],
:page => params[:page],
:per_page => 50,
:order => "field_a desc"
)
I've removed :sort_mode and :match_mode from the options you were using - the extended approaches are the only approaches available with Sphinx's SphinxQL protocol (and that's what TS v3 uses), so you don't need to specify them.
I've been deploying some apps to Heroku recently. I run MySQL on my local dev machine and have spent a little while updating some of my scopes to work in PostgreSQL. However one i have received an error on is proving difficult to change.
For the time being i've got a database specific case statement in my model. I understand why the error regarding the MySQL date functions is occurring, but im not sure if this is the most efficient solution. Does anyone have a better way of implementing a fix that will work with both MySQL and PostgreSQL?
case ActiveRecord::Base.connection.adapter_name
when 'PostgreSQL'
named_scope :by_year, lambda { |*args| {:conditions => ["published = ? AND (date_part('year', created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
named_scope :by_month, lambda { |*args| {:conditions => ["published = ? AND (date_part('month', created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
named_scope :by_day, lambda { |*args| {:conditions => ["published = ? AND (date_part('day', created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
else
named_scope :by_year, lambda { |*args| {:conditions => ["published = ? AND (YEAR(created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
named_scope :by_month, lambda { |*args| {:conditions => ["published = ? AND (MONTH(created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
named_scope :by_day, lambda { |*args| {:conditions => ["published = ? AND (DAY(created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
end
FYI, this is the PostgreSQL error that i am getting:
PGError: ERROR: function month(timestamp without time zone) does not exist LINE 1: ...T * FROM "articles" WHERE (((published = 't' AND (MONTH(crea... ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. : SELECT * FROM "articles" WHERE (((published = 't' AND (MONTH(created_at) = '11')) AND (published = 't' AND (YEAR(created_at) = '2010'))) AND ("articles"."published" = 't')) ORDER BY created_at DESC LIMIT 5 OFFSET 0
Thanks in advance for any input anyone has.
You should be using the standard EXTRACT function:
named_scope :by_year, lambda { |*args| {:conditions => ["published = ? AND (extract(year from created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
Both PostgresSQL and MySQL support it.
Unfortunately this happens alot, however you have the general right idea.
Your first method of attack is to see if there is a function that exists both in MySQL and Postres, however this isn't possible in this case.
The one suggestion I would make is that there is a lot of code duplication in this solution. Considering the condition statement is the only compatible issue here, I would factor out the compatiablity check only for the condition:
Example (Semi-Psuedo Code):
named_scope :by_year, lambda { |*args| {:conditions => ["published = ? AND (#{by_year_condition} = ?)", true, (args.first)], :order => "created_at DESC"} }
#...code...
def by_year_condition
if postgres
"date_part('year', created_at)"
else
"YEAR(created_at)"
end
Another option would be to create computed columns for each of your date parts (day, month, and year) and to query directly against those. You could keep them up to date with your model code or with triggers. You'll also get the benefit of being able to index on various combinations on your year, month, and day columns. Databases are notoriously bad at correctly using indexes when you use a function in the where clause, especially when that function is pulling out a portion of data from the middle of the column.
The upside of having three separate columns is that your query will no longer rely on any vendor's implementations of SQL.
I have the following tables:
User :has_many Purchases
Item :has_many Purchases
Item has a column "amount" (can be + or -) and I need to find all Users that have a positive SUM of "Item.amounts" (over all Purchases each one has made).
How does this query look like? (I'm not sure how to handle "SUM" correctly, in this case.)
I started out with the following, but obviously, it's wrong... (it wouldn't "include" Purchases that have an Item with a negative Item.amount...)
#users = User.find(:all,
:include => {:purchases => :item},
:select => "SUM(item.amount)",
:order => "...",
:conditions => "...",
:group => "users.id",
:having => "SUM(item.amount) > 0"
)
Thanks for your help with this!
Tom
Try this:
User.all(:joins => items, :group => "users.id",
:having => "SUM(items.amount) > 0")
It sounds like this is a good case for some model methods.
I didn't test this but I think you want to do something similar to the following:
class User < ActiveRecord::Base
has_many :purchases
has_many :items, :through => :purchases
def items_total
#get all the items iterate over them to get the amount,
#compact to get rid of nils
#and reduce with a sum function to total and return
items.all.each{|item| item.amount}.compact.reduce(:+)
end
then
User.items_total
PROBLEM:
I want to run a query which would trigger something like
select * from users where code in (1,2,4);
using a named_scope.
WHAT I TRIED:
This is for a single code:
named_scope :of_code, lambda {|code| {:conditions => ["code = ?", code]}}
I tried something like
named_scope :of_codes, lambda {|codes| {:conditions => ["code in ?", codes]}}
and sent
user.of_codes('(1,2,4)')
it triggers
select * from users where code in '(1,2,4)' which raises a MySQL error because of the extra quotes.
PS: Ideally I would like to send user.of_codes([1,2,4])
This will work just find and not expose you to the SQL injection attack:
named_scope :of_codes, lambda { |codes|
{ :conditions => ['code in (?)', codes] }
}
User.of_codes([1, 2, 3])
# executes "select * from users where code in (1,2,3)"
If you want to be a little more slick, you can do this:
named_scope :of_codes, lambda { |*codes|
{ :conditions => ['code in (?)', [*codes]] }
}
Then you can call it either with an Array (as above): User.of_codes([1, 2, 3]), or with a list of code arguments: User.of_codes(1, 2, 3).
The simplest approach would be to use a hash for conditions instead of an array:
named_scope :of_codes, lambda { |*codes| { :conditions => { :code => codes } } }
This will work as expected.
User.of_codes(1, 2, 3) # => SELECT ... code IN (1,2,3)
User.of_codes(1) # => SELECT ... code IN (1)
you can try follwing
named_scope :of_codes, lambda {|codes| {:conditions => ["code in "+codes]}}
and
user.of_codes('(1,2,4)')
EDITED For SQL INJECTION PROBLEM USE
named_scope :of_codes, lambda {|codes| {:conditions => ["code in (?) ", codes]}}
and
user.of_codes([1,2,4])