I upgraded to Rails 3.2.3 and all of a sudden this code no longer works:
def self.search(query, project_id, person_id)
if query
where("number LIKE ?", "%#{query}%")
elsif project_id
where("project_id LIKE ?", project_id)
elsif person_id
where("projects.person_id = ?", person_id)
else
scoped
end
end
It is the last where clause that triggers the error:
SQLite3::SQLException: no such column: projects.person_id: SELECT COUNT(DISTINCT "invoices"."id") FROM "invoices" LEFT OUTER JOIN "items" ON "items"."invoice_id" = "invoices"."id" LEFT OUTER JOIN "payments" ON "payments"."invoice_id" = "invoices"."id" WHERE "invoices"."user_id" = 1 AND (projects.person_id = '1')
In my models all the belongs_to and has_many statements are set correctly and it worked in my previous version of Rails (not sure which one that was though).
Can anybody tell me how to get this working again?
Thanks for any help.
I believe you'll have to join the projects table:
..
elsif person_id
joins(:projects).where("projects.person_id = ?", person_id)
else
..
Related
I am generating my query dynamically And this is something I have before passing parameters and getting this error pq: column reference "deleted_at" is ambiguous
My query is
SELECT COUNT(*)
FROM issues i
JOIN vehicles v ON (v.id = i.vehicle_id)
WHERE i.asset_owner_id = :asset_owner_id AND deleted_at IS NULL
AND i.added_at >= :from AND i.added_at < :to AND i.status IN (:status)
You have deleted_at in both tables. That is what "ambiguous" means here.
Qualify all column references when you write a query. I don't know which you intend, but something like this:
SELECT COUNT(*)
FROM issues i JOIN
vehicles v
ON v.id = i.vehicle_id
WHERE i.asset_owner_id = :asset_owner_id AND
i.deleted_at IS NULL AND
------^
i.added_at >= :from AND
i.added_at < :to AND
i.status IN (:status);
I have a Laravel API in which I want to provide a list of hashtags with the count of posts and comments in which a hashtag is used and order the list by the total count of posts and comments.
Here are my tables: posts, comments, hashtags, hashtag_post, comment_hashtag. Also, proper relationship methods are defined in my models.
After reading the Laravel's documentation and searching the web, I wrote this code:
Hashtag::withCount(['posts', 'comments'])
->orderByRaw('posts_count + comments_count desc')
->paginate(15);
If I use orderBy with only posts_count or comments_count alone, the query works and I get a list, but with the above code, I get this error:
Illuminate/Database/QueryException with message 'SQLSTATE[42703]: Undefined column: 7 ERROR: column "posts_count" does not exist ...
I've used tinker to see if those columns are defined properly on the SQL query, and here's what I've got:
select "hashtags".*, (select count(*) from "posts" inner join "hashtag_post" on "posts"."id" = "hashtag_post"."post_id" where "hashtags"."id" = "hashtag_post"."hashtag_id" and "posts"."deleted_at" is null) as "posts_count", (select count(*) from "comments" inner join "comment_hashtag" on "comments"."id" = "comment_hashtag"."comment_id" where "hashtags"."id" = "comment_hashtag"."hashtag_id" and "comments"."deleted_at" is null) as "comments_count" from "hashtags" where "hashtags"."deleted_at" is null order by posts_count + comments_count desc
I've searched the web about the error, but I found nothing useful.
Edit 1: Based on #Ersoy comment I've changed my query as follow:
Hashtag::selectRaw('Count(posts.id) + Count(comments.id) as total_count')
->withCount(['posts', 'comments'])
->groupBy('hashtags.id')
->orderByRaw('(Count(posts.id) + Count(comments.id)) desc')
->paginate(15);
And as I expected, it didn't work. It says:
Illuminate/Database/QueryException with message 'SQLSTATE[42P01]: Undefined table: 7 ERROR: missing FROM-clause entry for table "posts"
The thing is, likes and comments in the other question, as I understand from the query, are 1-N relationships while my relationships are N-M. Also, I think using the Count function like that won't work without a join. Correct me if I'm wrong.
Edit 2: One of my friends gave me this SQL query which gives the result I want:
select
h.*,
count(distinct hp.post_id) as posts_count,
count(distinct ch.comment_id) as comments_count
from
hashtags as h
left join hashtag_post as hp on
h.id = hp.hashtag_id
left join posts p on
hp.post_id = p.id
left join comment_hashtag ch on
h.id = ch.hashtag_id
left join comments c on
ch.comment_id = c.id
where
h.deleted_at is null
and p.deleted_at is null
and c.deleted_at is null
group by
h.id
order by
( count(distinct hp.post_id) + count(distinct ch.comment_id) ) desc;
But I don't know how to convert it properly to an Eloquent query.
DB::table('hashtags as h')
->leftJoin('hashtag_post as hp', 'h.id', '=' ,'hp.hashtag_id')
->leftJoin('posts as p', 'hp.post_id', '=' ,'p.id')
->leftJoin('comment_hashtag as ch', 'h.id', '=', 'ch.hashtag_id')
->leftJoin('comments as c', 'ch.comment_id', '=' ,'c.id')
->whereNull('h.deleted_at')
->whereNull('p.deleted_at')
->whereNull('c.deleted_at')
->selectRaw("*, count(distinct hp.post_id) + count(distinct ch.comment_id) as total_count")
->groupBy('h.id')
// try
->orderByRaw('count(distinct hp.post_id) + count(distinct ch.comment_id) desc')
// or
->orderByRaw('total_count desc')
->get();
Both the orderByRaw statement are similar, you can try any of the above. Hopefully, it should work.
The following code works perfectly if the Laravel version is 6+:
Hashtag::withCount(['posts', 'comments'])
->orderByRaw('posts_count + comments_count desc')
->paginate(15);
However, I'm currently using Laravel 5.7 and I can't upgrade it now. Thus I should use an alternative to get the desired result:
Hashtag::leftJoin('hashtag_post as hp', 'hashtags.id', '=', 'hp.hashtag_id')
->leftJoin('posts as p', 'hp.post_id', '=', 'p.id')
->leftJoin('comment_hashtag as ch', 'hashtags.id', '=', 'ch.hashtag_id')
->leftJoin('comments as c', 'ch.comment_id', '=', 'c.id')
->whereNull('p.deleted_at')
->whereNull('c.deleted_at')
->selectRaw('hashtags.*, count(distinct hp.post_id) as posts_count, count(distinct ch.comment_id) as comments_count')
->groupBy('hashtags.id')
->orderByRaw('count(distinct hp.post_id) + count(distinct ch.comment_id) desc')
->paginate(15)
Thanks to #tarun-jain
If this query would be done in Rails ORM, it would look like
ids = User.find(user_id).conversations.pluck(:id)
UserMessage.where("'conversation'.'id' IN (?)", ids)
My models are:
class User
has_many :conversations, through: :user_conversations
end
class UserMessage
belongs_to :user
belongs_to :conversation
end
In raw SQL:
ActiveRecord::Base.connection.execute(
%( SELECT "user_messages".*,
( SELECT array_agg(t)
FROM (
SELECT "conversations"."id"
FROM "conversations"
INNER JOIN "users_conversations" ON "conversations"."id" = "users_conversations"."conversation_id"
WHERE "users_conversations"."user_id" = 'f044e064-0b6f-4371-91aa-3c03e31c8ad8'
) t
) AS this_user_conversations
FROM "user_messages"
WHERE "user_messages"."conversation_id" IN (this_user_conversations)))
this gives
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "this_user_conversations" does not exist
LINE 11: WHERE "user_messages"."conversation_id" IN (this_user_conver...
But well as far as I understand, this query defines this value with AS statement.
The where clause happens before the select list so it can't see the subquery. An alternative:
select
um.*, tuc.this_user_conversations
from
"user_messages" um
inner join
(
select array_agg(c."id") as this_user_conversations
from
"conversations" c
inner join
"users_conversations" uc on c."id" = uc."conversation_id"
where uc."user_id" = 'f044e064-0b6f-4371-91aa-3c03e31c8ad8'
) tuc on um."conversation_id" = any (tuc.this_user_conversations)
I'm trying to write the following query using the ruby gem Squeel
SELECT COUNT(*)
FROM(
SELECT
a.end_at AS START,
Min(b.start_at) AS END
FROM periods AS a
JOIN periods AS b ON b.season_id IN (1,2,3) AND a.end_at <= b.start_at
WHERE a.season_id IN (1,2,3)
GROUP BY a.end_at
HAVING a.end_at < MIN(b.start_at)
) AS gaps
WHERE
gaps.START < '2013-05-17' AND gaps.END > '2013-05-05';
Any idea on how to achieve this?
I can get the seasons_ids using:
seasons = self.seasons
Period.where{season_id.in(seasons.select{id})}
but the self join with condition, I have no idea how to tackle this so far.
This is similar to what I've tried to do here.
What I would try is using a sub-query, joining Period to itself as follows:
class Period < ActiveRecord::Base
attr_accessible :end_at, :season_id, :start_at
belongs_to :season
scope :in_seasons, ->(season_ids) { joins{season}.where{id.in(season_ids)} }
def self.find_gaps(start_date, end_date)
season_ids = ["1", "2", "3"]
scope = select{[end_at.as(`gap_start`), `min(self_join.start_at)`.as(`gap_end`)]}
scope = scope.joins{"LEFT JOIN (" + Period.in_seasons(season_ids).to_sql + ") AS self_join ON self_join.end_at <= periods.start_at"}
scope = scope.where{(`self_join.start_at` != nil) & (`gap_start` < start_date) & (`gap_end` > end_date)}
scope = scope.group{`gap_end`}.having{end_at < `min(self_join.start_at)`}
end
end
In rails console, Period.find_gaps('2001-01-01', '2010-01-01').to_sql produces (formatting is my own):
SELECT
\"periods\".\"end_at\" AS gap_start,
min(self_join.start_at) AS gap_end
FROM \"periods\"
LEFT JOIN
(SELECT \"periods\".*
FROM \"periods\"
INNER JOIN \"seasons\" ON \"seasons\".\"id\" = \"periods\".\"season_id\"
WHERE \"periods\".\"id\" IN (1, 2, 3)
) AS self_join ON self_join.end_at <= periods.start_at
WHERE ((self_join.start_at IS NOT NULL AND gap_start < '2001-01-01' AND gap_end > '2010-01-01'))
GROUP BY gap_end
HAVING \"periods\".\"end_at\" < min(self_join.start_at)
It looks like what you are trying to get at... at least the inner portion.
Hope it helps.
I'm using Spring JDBC and oracle SQL.
using the SpringJDBC class MapSqlParameterSource, i have mapped the data i want to merge.
Now i want to use the merge statement to update/insert database table. All i have is one table and a bunch of parameters i want to merge into it.
merge into proj.person_registry pr
using ( ! parameters should go here somehow? )
on (pr.id = :id or pr.code = :code)
when matched then
update set pr.code = :code,
pr.name = :name,
pr.firstname = :firstname,
pr.cl_gender = :cl_gender,
pr.cl_status = :cl_status,
pr.aadress = :aadress,
pr.aadress_date = :aadress_date
when not matched then
insert values (:code, :name, :firstname, :cl_gender, :cl_status, ;aadress, :aadress_date);
Do i have to somehow create a temporary table for the using keyword or is there another way? how would i go about merging like this?
also there are two unique keys pr.id and pr.code. Sometimes the parameter :id is null, when this happens I want to reach the update statement based on pr.code getting matched to :code. Will it still work if my update contains the line:
update set pr.code = :code,
This should work:
merge into proj.person_registry pr
using (
select 42 as id
'xyz' as code,
'Dent' as name,
'Arthur' as firstname,
'male' as cl_gender
'closed' as cl_status,
'Somewher' as aaddress,
current_date as aaddress_date
from dual
) t on (pr.id = t.id or pr.code = t.code)
when matched then
update set pr.code = t.code,
pr.name = t.name,
pr.firstname = t.firstname,
pr.cl_gender = t.cl_gender,
pr.cl_status = t.cl_status,
pr.aadress = t.aadress,
pr.aadress_date = t.aadress_date
when not matched then
insert values (t.code, t.name, t.firstname, t.cl_gender, t.cl_status, ;aadress, t.aadress_date);
I'm not familiar with Spring's JDBC template, but replacing the actual values in the select ... from dual query by parameter placeholders should work.