In my controller, my filter_with_params() method is causing a syntax error in postgres when I try and stack will_paginate on top of it.
In my controller I call:
#styles = Style.filter_with_params(params).paginate(:page => params[:page], :per_page => 6)
Model method:
class Style < ActiveRecord::Base
def self.filter_with_params(params)
scoped = where("styles.item != ''")
if params[:t]
scoped = scoped.joins(:tags)
scoped = scoped.select("distinct styles.*, count(*) AS count")
scoped = scoped.where(tags: { name: params[:t] })
scoped = scoped.group('styles.id')
scoped = scoped.having("count(*) = #{params[:t].size}")
end
scoped
end
basically my filter method is looking for tags, and then i need to paginate on top of those results. Anyone with similar experience?
I'm using Rails 3 on this app
here is the postgres error
PG::Error: ERROR: syntax error at or near "distinct" LINE 1: SELECT COUNT(*) AS count_all, distinct styles.*, count(*) AS...
^
: SELECT COUNT(*) AS count_all, distinct styles.*, count(*) AS count, styles.id AS styles_id FROM "styles" INNER JOIN "tagizations" ON "tagizations"."style_id" = "styles"."id" INNER JOIN "tags" ON "tags"."id" = "tagizations"."tag_id" WHERE "tags"."name" IN ('engagement') AND (styles.polenza_item != '') GROUP BY styles.id HAVING count(*) = 1
Your SQL has a problem. You need to say distinct clause before the count ('count(*) as count_all'). That is, once you remove the first call to the count function, it should work.
SELECT distinct styles.*, count(*) AS count, styles.id AS styles_id FROM "styles" INNER JOIN "tagizations" ON "tagizations"."style_id" = "styles"."id" ...
You can test your query in your rails console:
>> sql = "SELECT distinct styles.*, count(*) AS count, styles.id AS styles_id..."
>> a = ActiveRecord::Base.connection.execute(sql)
>> a[0]
Hope this helps.
Related
user has_many tasks
I'm trying to create a 3 select UNION:
Task.from("(#{select1.to_sql} UNION #{select2.to_sql} UNION #{select3.to_sql}) AS tasks")
But with Arel.
I can easily use Arel for UNION query for 2 selects:
tasks_t = Task.arel_table
select1 = tasks_t.project('id').where(tasks_t[:id].eq(1)) # SELECT id FROM tasks WHERE tasks.id = 1
select2 = Task.joins(:user).select(:id).where(users: {id: 15}).arel # SELECT "tasks".id FROM "tasks" INNER JOIN "users" ON "users"."id" = "tasks"."user_id" WHERE "users"."id" = 15
union = select1.union(select2) # SELECT * FROM users WHERE users.id = 1 UNION SELECT * FROM users WHERE users.id = 2
union_with_table_alias = tasks_t.create_table_alias(union, tasks_t.name)
Task.from(union_with_table_alias)
# SELECT "tasks".* FROM ( SELECT id FROM "tasks" WHERE "tasks"."id" = 1 UNION SELECT "tasks"."id" FROM "tasks" INNER JOIN "users" ON "users"."id" = "tasks"."user_id" WHERE "users"."id" = $1 ) "tasks" [["id", 15]]
#=> Task::ActiveRecord_Relation [#<Task: id: 1>, #<Task: id: 2>]
How can I do it with triple union select?
select3 = tasks_t.project('id').where(tasks_t[:id].eq(3)) # SELECT id FROM tasks WHERE tasks.id = 3
Something like:
triple_union = select1.union(select2).union(select3)
triple_union_with_table_alias = tasks_t.create_table_alias(triple_union, tasks_t.name)
Task.from(triple_union_with_table_alias)
Which should roughly translate to
Task.from("(#{select1.to_sql} UNION #{select2.to_sql} UNION #{select3.to_sql}) AS tasks")
Note the above line also fails:
Caused by PG::ProtocolViolation: ERROR: bind message supplies 0 parameters, but prepared statement "" requires 1
To summarize:
How to use Arel to build a tripple UNION query? select 1 UNION select 2 UNION select 3
Thank you
Ruby 2.5.3 Rails 5.2.4 Arel 9.0.0
You can not invoke union in an Arel::Nodes::Union, because in the method definition the ast of both objects is called. And the result of a union operation doesn't respond to ast:
def union(operation, other = nil)
if other
node_class = Nodes.const_get("Union#{operation.to_s.capitalize}")
else
other = operation
node_class = Nodes::Union
end
node_class.new(self.ast, other.ast)
end
What you can do is to manually call Arel::Nodes::Union, passing as any of those arguments the result of a union:
Arel::Nodes::Union.new(Arel::Nodes::Union.new(select1, select2), select3)
In Codeigniter3, I have the following select join model:
public function get_post()
{
$q = $this->db->select('posts.id, posts.active, posts.title, posts.close_date, users.name')
->from('posts')
->join('users', 'posts.user_id = users.id', 'left')
->where('posts.active','1')
->where(['posts.active' => '1', 'posts.close_date >=' => date('Y-m-d')])
->order_by('posts.updated_at', 'DESC')->get();
return $q;
}
How can I list all posts with count posts for each user (count only posts where posts.active = 1 and posts.close_date >= current date)?
If CI can't do the job, please give me the sql query.
Here you go:
With complex queries like this, the best way is to store the sql script in a string variable and use $this->db->query() to execute it.
This query is in MySQL:
public function get_posts() {
$sql = "
SELECT `posts`.`id`, `posts`.`active`, `posts`.`title`, `posts`.`close_date`, users.name, COUNT(`posts`.`user_id`) AS `active_post_count`
FROM `posts`
LEFT JOIN `users` ON `posts`.`user_id` = `users`.`id`
WHERE `posts`.`active` = ?
AND `posts.close_date` >= ?
ORDER BY `posts`.`updated_at` DESC;
";
$now = new DateTime('now');
$binds = [1, $now->format('Y-m-d H:i:s')];
return $this->db->query($sql, $binds)->result_array();
}
Here's a sample code on how to access the post count:
$this->Your_model->get_posts()[0]['active_post_count'];
See CodeIgniter's docs on Generating Query Results:
If you want the result as an object instead, use result_object() over result_array().
I'm trying to do the following, and if I were to uncomment the distinct it will break. Also if I comment out the order and leave the distinct in, it will work.
Contestant.joins('INNER JOIN votes AS V ON V.contestant_id = contestants.id AND V.season_id = '+ season_number.to_s)
.joins('LEFT OUTER JOIN votes AS XV ON (XV.contestant_id = '+self.id.to_s+') AND (XV.tribal_council_key = V.tribal_council_key) AND XV.contestant_voted_for_id = V.contestant_voted_for_id')
.joins('INNER JOIN season_rosters ON season_rosters.season_id = V.season_id')
.where('V.is_jury_vote = (?) AND V.contestant_id <> (?) AND XV.tribal_council_key IS NOT NULL', :false, self.id)
.order('season_rosters.finished')
#.distinct
The error I get is below...
TinyTds::Error: Incorrect syntax near '*'.: EXEC sp_executesql N'SELECT DISTINCT *, __order FROM ( SELECT [contestants].*, DENSE_RANK() OVER (ORDER BY season_rosters.finished ASC) AS __order, ROW_NUMBER() OVER (PARTITION BY [contestants].* ORDER BY season_rosters.finished ASC) AS __joined_row_num FROM [contestants] INNER JOIN votes AS V ON V.contestant_id = contestants.id AND V.season_id = 6 LEFT OUTER JOIN votes AS XV ON (XV.contestant_id = 112) AND (XV.tribal_council_key = V.tribal_council_key) AND XV.contestant_voted_for_id = V.contestant_voted_for_id INNER JOIN season_rosters ON season_rosters.season_id = V.season_id WHERE (V.is_jury_vote = (''false'') AND V.contestant_id <> (112) AND XV.tribal_council_key IS NOT NULL) ) AS __sq WHERE __joined_row_num = 1 ORDER BY __order'
The issue is with this part:
SELECT DISTINCT *, __order
Try adding the required columns to your GROUP BY.
Contestant.joins('INNER JOIN votes AS V ON V.contestant_id = contestants.id AND V.season_id = '+ season_number.to_s)
.joins('LEFT OUTER JOIN votes AS XV ON (XV.contestant_id = '+self.id.to_s+') AND (XV.tribal_council_key = V.tribal_council_key) AND XV.contestant_voted_for_id = V.contestant_voted_for_id')
.joins('INNER JOIN season_rosters ON season_rosters.season_id = V.season_id')
.where('V.is_jury_vote = (?) AND V.contestant_id <> (?) AND XV.tribal_council_key IS NOT NULL', :false, self.id)
.order('season_rosters.finished')
.group('col1,col2,__order')
Also in your SQL error, order by is on a different column while in your code, it is on season_rosters.finished.
I have an sql query like this:-
SELECT * FROM `permissions` join entities where NOT EXISTS (select
entity_id,permission_id from role_permissions where role_id=5 and
entities.id = role_permissions.entity_id and permissions.id =
role_permissions.permission_id)
I would like to get the corresponding rails query.
I have tried this.
Permission.joins("join entities").joins("LEFT OUTER JOIN role_permissions on
permission_id != permissions.id and entities.id != entity_id and
role_permissions.role_id= role_id").select("role_permissions.entity_id,role_permissions.role_id,
role_permissions.permission_id").group('role_permissions.entity_id,
role_permissions.permission_id')
But it doesn't works.
thanks
hari
I have been particularly in love with EXISTS queries lately, precisely because it does not require you to fully join another table. As far as I know, you do have to explicitly write the SQL clause, but you can still make it work with Activerecord. You can even put this in a scope within a lambda block.
Permission.joins(:entities).where(<<-SQL
NOT EXISTS(
select
* from role_permissions where role_id=#{your_role_id} and
entities.id = role_permissions.entity_id
and permissions.id = role_permissions.permission_id
)
SQL
)
Try like this for a default SQL query:
sql = "SELECT some_field FROM `permissions` join entities where NOT EXISTS (select entity_id,permission_id from role_permissions where role_id=5 and entities.id = role_permissions.entity_id and permissions.id = role_permissions.permission_id)"
results = ActiveRecord::Base.connection.execute(sql)
results.each do |result|
#register_users << { some_field: result[0] }
end
Try this
UPDATED BASED ON FIRST COMMENT
Permission.joins(:entities).joins("LEFT OUTER JOIN role_permissions on
permission_id != permissions.id and entities.id != entity_id and
role_permissions.role_id= role_id")
.select("role_permissions.entity_id,role_permissions.role_id,
role_permissions.permission_id")
.group('role_permissions.entity_id, role_permissions.permission_id')
i am using postgres and wishing to exclude users that are currently in one table from another. at present i am trying do this via the ActiveRecord system within Rails.
So i need it to get the ids from my Availability table, then return that id into my User table to remove them if they are in the Availability table.
#availabilities = Availability.where(:event_id => params[:id]).all
#players = User.where('team_id = ? and id <> ?', current_user[:team_id], #availabilities).all
this is returning the following error
PG::Error: ERROR: argument of WHERE must be type boolean, not type record
LINE 1: SELECT "users".* FROM "users" WHERE (team_id = 1 and id <> ...
^
: SELECT "users".* FROM "users" WHERE (team_id = 1 and id <> 101,102,103)
changed code as mentioned below, though the way i am doing it is still probably not ideal
#availabilities = Availability.where(:event_id => params[:id]).all
#exclude = Availability.where(:event_id => params[:id]).select(:user_id).pluck(:user_id)
if #exclude.count > 0
#players = User.where('team_id = ? and id NOT IN (?)', current_user[:team_id], #exclude).all
else
#players = User.where('team_id =?', current_user[:team_id])
You could do something like this:
#availabilities = Availability.where(event_id: params[:id]).pluck(:id)
#players = User.where(team_id: current_user[:team_id])
#players = #players.where('id NOT IN (?)', #availabilities) unless #availabilities.empty?
Using pluck() will return an array of IDs, then you can exclude them by using NOT IN (?)
Try:
id not in
The way pg engine sees it is ((team_id=1 and id <> 101), 102, 103). Thus the error you see.
Use it as:
User.where('team_id = ? and id not in (?)', current_user[:team_id], #availabilities).all