Alternative as NOT IN gives a blank array - mysql

I have a query in my rails application,
user_info = Information.where("user_id = ? and department_id NOT IN (?)",user.id, Department.unapproved_departments ).all(:include => [:name, :project]).
Here unapproved_departments contains an array of ids in Department table.
The problem arises when unapproved_departments is a blank array.In those cases "NOT IN"
checks for ids present in the array and entire user_info is returned as a blank array.
So i would like to have all the data with ids other than those in unapproved_departments array or you can say an alternative for NOT IN
Thanx

Theres nothing wrong with NOT IN -- just that you don't want it to fire when there's nothing there.
I suggest:
user_info = Information.where("user_id = ?", user)
unapproved_departments = Department.unapproved_departments
if unapproved_departments.present? # true if not blank or nil
user_info = user_info.where("department_id NOT IN (?)", unaproved_departments)
end
user_info.all(:include => [:name, :project])
As you can see, you can chain the conditions together.

Can't you just have a special case for when unapproved_departments is empty?
if Department.unapproved_departments.empty?
user_info = Information.where("user_id = ?",user.id).all(:include => [:name, :project])
else
user_info = Information.where("user_id = ? and department_id NOT IN (?)",user.id, Department.unapproved_departments ).all(:include => [:name, :project])
end

it will be better if you define better unapproved_departments to retun [nil] instead of []
Oterwhise you can add [0] to your code :
user_info = Information.where("user_id = ? and department_id NOT IN (?)",user.id, (Department.unapproved_departments << 0) ).all(:include => [:name, :project]).

If we concentrate just on the "NOT IN" part:
user_info = Information.where("department_id NOT IN (?)", []).to_sql
# => SELECT `informations`.* from `informations` where department_id not in (NULL)
Which is weird because of null-tests: http://dev.mysql.com/doc/refman/5.0/en/working-with-null.html
What we can do is reverse the test and get the required result:
user_info = Information.where("NOT (department_id IN (?))", []).to_sql
# => SELECT `informations`.* from `informations` where NOT (department_id in (NULL))
It works because in MySQL everything is != NULL (and also < NULL and > NULL at same time!).

Related

Ruby On Rails and redmine Aglie plugin fixing

Well, as maybe you know guys, in redmine base sturcture there are tables issues, projects. I never used to write code in ruby before btw. But I got task, so.
Here the issues table structure:
And here is the project table structure:
The problem is I need to get all issues where project_id equals #project and project.parent_id equals #project too.
The problem is, first I don't understand what current #project contents. Second I got no idea how to access to the #project's parent_id field here. Parent_id is referenced to the project.id field by the way.
I got ruby function here.
Boss said I need to edit scope variable. So I trying to edit it like this:
def find_no_version_issues
#backlog_version = #project.versions.open.where("LOWER(#{Version.table_name}.name) LIKE LOWER(?)", "backlog").first ||
#project.versions.open.where(:effective_date => nil).first ||
Version.open.where(:project_id => #project).order("effective_date ASC").first
#current_version = Version.open.
where(:project_id => #project).
where("#{Version.table_name}.id <> ?", #backlog_version).
order("effective_date DEcSC").first
q = (params[:q] || params[:term]).to_s.strip
#scope = (params[:scope] == "all" || #project.nil? ? Issue : #project.issues).open.visible.where(:fixed_version_id => nil).sorted_by_rank
scope = Issue.open.visible.where(:fixed_version_id => nil).where("project_id = ?", ) OR #project.or(:project_id.parent_id => #project)).sorted_by_rank
#scope = Issue.open.visible.where(:project_id => #current_version) #(:project_id => #project || #project.parent_id == :project_id || #project ).sorted_by_rank
if q.present?
if q.match(/^#?(\d+)\z/)
scope = scope.where("(#{Issue.table_name}.id = ?) OR (LOWER(#{Issue.table_name}.subject) LIKE LOWER(?))", $1.to_i, "%#{q}%")
else
scope = scope.where("LOWER(#{Issue.table_name}.subject) LIKE LOWER(?)", "%#{q}%")
end
end
#issue_count = scope.count
#issue_pages = Redmine::Pagination::Paginator.new #issue_count, 20, params['page']
#version_issues = scope.offset(#issue_pages.offset).limit(#issue_pages.per_page).all
end
So, please, could somebody help me? How could I understand, what #project contains? How could I turn query into scope?
Here's working SQL-query (or at least it got rows I need). But I really have no idea how to make this scope varible contains this data?
SELECT * FROM `issues` WHERE `project_id` IN (SELECT `id` FROM `projects` WHERE `parent_id` IS NOT NULL OR `parent_id` IS NULL ) ORDER BY `id` ASC
Well, actually I fixed this thing this way
def find_no_version_issues
q = (params[:q] || params[:term]).to_s.strip
#scope = (params[:scope] == "all" || #project.nil? ? Issue : #project.issues).open.visible.where(:fixed_version_id => nil).sorted_by_rank
#subprojects = #project.descendants.visible.all
#subprojects = #subprojects.to_a.push #project
scope = Issue.open.visible.where(project_id:#subprojects).where(:fixed_version_id => nil).sorted_by_rank
if q.present?
if q.match(/^#?(\d+)\z/)
scope = scope.where("(#{Issue.table_name}.id = ?) OR (LOWER(#{Issue.table_name}.subject) LIKE LOWER(?))", $1.to_i, "%#{q}%")
else
scope = scope.where("LOWER(#{Issue.table_name}.subject) LIKE LOWER(?)", "%#{q}%")
end
end
#issue_count = scope.count
#issue_pages = Redmine::Pagination::Paginator.new #issue_count, 20, params['page']
#version_issues = scope.offset(#issue_pages.offset).limit(#issue_pages.per_page).all
end

Activerecord OR condition for a Joined table's attribute

I would like to write an query returning
All tasks where a user_id = self.id
OR
all tasks whereby the associated trip's booker_id = self.id
Task.joins(:trip).where('user_id = ? OR trip.booker_id = ?', self.id, self.id)
The above returns the error
PG::AmbiguousColumn: ERROR: column reference "user_id" is ambiguous
Any help / explanation would be appreciated
You have user_id column in your trips table, so PG doesn't know what you mean when you write user_id = .... You need to specify table name, like here:
Task.joins(:trip).where('tasks.user_id = :id OR trip.booker_id = :id', id: self.id)

Sort 2 ActiveRecord arrays depending on :user_id and then merge into 1

I am trying to build a "friending" function between users in my Rails app.
When a user makes a friend request, a new "Friend" Item is created:
#friend = Friend.create(:user_id => session[:user_id], :friend_id => params[:friend_id], :status => "pending")
The other user (:friend_id) can then accept or decline the request. On accept the status is changed to "accepted" and all of that works fine.
I'm trying to return a list of friends now but the problem that I have is that the Friend item can either hold the friends id in :user_id or :friend_id depending on who initiated the request.
I am returning all Friend items that have the logged in user id in either :user_id or :friend_id but i'm having trouble sorting these into 1 list of users that are not the currently logged in user (i.e users that are friends of the logged in user) once I have those Friend items. This is what I have so far, hopefully it will give you an idea of what I am trying to do.
#stalked = Friend.where(:user_id => session[:user_id], :status => "accepted")
# #stalked_friends = Users where id == :friend_id in #stalked list
#stalkers = Friend.where(:friend_id => session[:user_id], :status => "accepted")
# #stalker_friends = Users where id == :user_id in #stalkers list
#friends = #stalked_friends + #stalker_friends
I hope this makes sense and thanks in advance
I have tried this on a small set of data and it worked fine, it should work!
cuid = session[:user_id]
query = %Q{SELECT DISTINCT users.*
FROM users,friends
WHERE (friends.user_id = #{cuid} OR friends.friend_id = #{cuid})
AND (friends.user_id = users.id OR friends.friend_id = users.id)
AND (friends.status = 'accepted')
AND (users.id != #{cuid})
}
#friends = User.find_by_sql(query)
This should do it:
#friends = Friend.where("#{Friend.table_name}.id <> ?", session[:user_id])
.where(status: 'accepted')
.where("#{Friend.table_name}.user_id = :id OR #{Friend.table_name}.friend_id = :id", id: session[:user_id])
This #friends is still an ActiveRecord::Relation object, so you can do scope-chaining after that, add more where clause, add an order strategy, etc.
#friends = [logic before]
# reorder the friends list by id
#friends = #friends.order(:id)
# limit the friends list to 20 max
#friends = #friends.limit(20)

Checking if a boolean column is true in MySQL/Rails

Rails and MySQL:
I have a table with several boolean columns representing tags. I want to find all the rows for which a specific one of these columns is 'true' (or I guess in the case of MySQL, '1'). I have the following code in my view.
#tag = params[:tag]
#supplies = Supply.find(:all,
:conditions=>["? IS NOT NULL and ? !=''", #tag, #tag], :order=>'name')
The #tag is being passed in from the url. Why is it then that I am instead getting all of my #supplies (i.e. every row) rather than just those that are true for the column for #tag.
Thanks!
If params[:tag] is set to foo, the find method is generating this query:
select * from supplies where 'foo' is not null and 'foo' != '' order by name;
This is returning all your Supply records because both conditions are always true.
'foo' is not null
'foo' != ''
Of course you're intending for params[:tag] to be a column name, but that is just terrible design.
You should have a tag attribute on your Supply model and use the following finder:
#supplies = Supply.all(:conditions => ["tag = ?", params[:tag]], :order => "name")
If you really want the ability for Supplies to have the option for multiple Tags, use:
class Supply < ActiveRecord::Base
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :supplies
end
#supplies = Supplies.all(:conditions => {:tags => ['foo', 'bar']}, :order => "name")
I think this is what you want to do
#tag = params[:tag]
#supplies = Supply.find(:all, :conditions=>["? = ?", #tag, true], :order=>'name')

Rails find :conditions

I have a Reservation model that I'm searching for with three fields. The container_id must always be self.id but as confirmed and auto_confirmed only one needs to be true. I have the following but it doesn't perform what I need:
Reservation.find(:all,
:conditions => ['container_id = ? AND confirmed = ? OR auto_confirm = ?',
self.id, true, true,])
How should I change this?
I'm not sure I'm getting your problem, but from what I understand this would work:
Reservation.find(:all,
:conditions => ['container_id = ? AND (confirmed = ? OR auto_confirm = ?)',
self.id, true, true,])
As per your question confirmed and auto_confirmed only one needs to be true. So use following
Reservation.find(:all,
:conditions => ['container_id = :container AND
( (confirmed = :flag and auto_confirm != :flag) ||
(confirmed != :flag and auto_confirm = :flag))',
{:container=> self.id, :flag=>true}]
)
I'm not sure if this is database agnostic, but you could try
Reservation.find(:all,
:conditions => ['container_id = ? AND confirmed = ? **XOR** auto_confirm = ?',
self.id, true, true,])
What you are saying is not true - a query like
SELECT * FROM foos WHERE content_id = 345 AND (confirm = 1 OR auto_confirm = 1)
will select the rows where both "confirm" columns are set to 1 (and ActiveRecord creates tinyint columns for booleans and checks against 1 and 0).
If you mean "find all rows matching on the content_id and having EITHER confirmed OR auto_confirmed true but NOT both" then you come down to a query like this
SELECT * FROM foos WHERE content_id = 345 AND ((confirmed = 1 AND auto_confirm = 0) OR (confirmed = 0 AND auto_confirm = 1))
which you reword in AR terms like this
Reservation.find(:all,
:conditions => [
'container_id = ? AND ((confirmed = 1 AND auto_confirm != 1) OR (confirmed = 0 AND auto_confirm != 1))',
self]
)
However, judging from the names your fields have you are actually implementing a state machine with separate columns which will bring you pain, so I'd investigate something that would give you progressions of states instead of checking for separate on and off bits.
I think something like this:
Reservation.find(:all,
:conditions => ['container_id = ? AND ((confirmed != true AND auto_confirm = true) OR (confirmed = true AND auto_confirm != true))',
self.id])