I'm trying to build the following query in Arel:
select a.* from (first nested query) as a
left outer join (second nested query) as b
on a.id = b.id
where b.id is null;
This is my best attempt:
query = a.
project(a[Arel.star]).
from(a_nested_sql).
join(b_nested_sql, Arel::Nodes::OuterJoin).
on(a[:id].eq(b[:id])).
where(b[:id].eq(nil))
But it keeps dropping the 'left outer join' producing invalid SQL:
select a.* from (first nested query) as a
(second nested query) as b
on a.id = b.id
where b.id is null;
What am I doing wrong?
After a lot of experimenting, this did the trick:
query = a_table.
project(a_table[Arel.star]).
from(a_nested_sql).
join(
b_table.join(b_nested_sql).join_sources,
Arel::Nodes::OuterJoin
).
on(a_table[:id].eq(b_table[:id])).
where(b_table[:id].eq(nil))
Not really sure why as I don't really understand what join_sources does.
Related
Right now I have:
select
a.id
b.colone
b.coltwo
from tablea a
left join tableb b on b.id = a.id
I'm getting the "numeric value not recognized" error because one value from b.id is not numeric. How do I join results from tableb to tablea just omitting the non-numeric value/row? Or how else can I bypass this error? Using snowflake.
Thank you!
You can use TRY_TO_NUMBER():
select
a.id
b.colone
b.coltwo
from tablea a
left join tableb b on TRY_TO_NUMBER(b.id) = a.id
I have 3 tables (a, b, c) and "a" has_many "b" and "b" has_many "c". I have this line c.includes(b: :a), but I need to count how many "a" are for each "c" so the results be like 2 columns. Also there are some data in C that doesn't have b, so when I show them, it throw me an error.
I can do it with a query from pgAdmin and it show me how I need it but I can't make it on ActiveRecords.
The query that works fine is:
select count(c.id), a.name from c
left Join b on b.id = c.b_id
left join a on a.id = b.a_id
where c.created_at between '2018-11-08 00:00:00' and '2018-11-08 23:59:59' group by a.name
A.
joins(:b).
joins(b: :c).
group("a.name").
where("c.created_at between ? and ?", DateTime.new(2018,
11, 08), DateTime.new(2018, 11, 08).end_of_day).
count
returns a hash where the keys are "a.name" and the values are the count of C for each A.
If you only want to count unique C for each A, you can specify count("distinct c.id").
For more complex queries you can always query with raw SQL:
ActiveRecord::Base.connection.execute(<<~SQL).values
select count(c.id), a.name from c
left Join b on b.id = c.b_id
left join a on a.id = b.a_id
where c.created_at between '2018-11-08 00:00:00' and '2018-
11-08 23:59:59' group by a.name
SQL
returns a jagged array of the results.
As for your orphaned C records, when you query for them you can inner join to B. That will only return C that have B.
I am struggling to use Rails' ActiveRecord query interface to replicate a query that has an inner join subquery. How would I replicate the following:
SELECT ass.name, COUNT(DISTINCT a.question_id) AS
answered_questions, tq.total_questions
FROM assessments AS ass
INNER JOIN (SELECT ass.id, COUNT(q.id) AS total_questions FROM
questions AS q INNER JOIN assessments AS ass ON ass.id=q.assessment_id
GROUP BY
ass.id) as tq ON tq.id=ass.id
INNER JOIN questions AS q ON q.assessment_id=ass.id
INNER JOIN answers AS a ON a.assessment_id=ass.id AND a.question_id=q.id
INNER JOIN org_assesments AS oa ON ass.id=oa.assessment_id
INNER JOIN users AS u ON oa.org_id=u.org_id AND
a.user_id=u.id
WHERE u.id=1
GROUP BY ass.name, tq.total_questions
ORDER BY ass.created_at DESC
LIMIT 10
I don't seem to be able to get this to work with the subquery using the query builder. Without the subquery I have this, which works and gives me the assessment title and number of questions answered:
Question.joins(:assessment => {:org_assessments => {:org => :users}}).joins(:answers)
.where(answers:{:user_id => params[:id]})
.distinct('answers.question_id').group(['assessments.name']).count()
How can I write this to include the subquery as in the original SQL above?
You may send the subquery as a string to the joins method:
subquery =
TotalQuestion.
joins(:assessments).
group('assessments.id').
select('assessments.id, COUNT(q.id) as total_questions').to_sql
Question.joins("(#{sub_query}) as tq on tq.id=ass.id")
And you can combine it with the other parts of the query:
Question.
joins(:assessment => {:org_assessments => {:org => :users}}).joins(:answers).
joins("(#{sub_query}) as tq on tq.id=ass.id").
where(answers:{:user_id => params[:id]}).
distinct('answers.question_id').group(['assessments.name']).count()
I'm trying to understand why I don't receive any records on a ruby on rails app using postgresql. This is the SQL query that is being executed:
SELECT g.program_id, g.title,
COALESCE(COUNT(pr), 0) AS ac, g.default
FROM groups AS g
LEFT OUTER JOIN memberships AS m ON m.group_id = g.id
LEFT OUTER JOIN progresses AS pr ON m.id = pr.participant_id
AND (pr.status = 'completed')
WHERE g.program_id = ANY(#1)
GROUP BY g.id
ORDER BY g.program_id, g.position, g.id
My question is: what does the ANY(#1) means?
Please have pacience, as I'm a ruby/rails/postgresql newbie.
Thanks!
Update: added some aditional code. Plese don't ident the query below as it is already idented above.
class StatsComponents::CompletedActivitiesPerGroupStats
include StatsComponent::Interface
GROUP_ACTIVITIES = <<-SQL
g.program_id, g.title, COALESCE(COUNT(pr), 0) AS ac, g.default
FROM groups AS g
LEFT OUTER JOIN memberships AS m ON m.group_id = g.id
LEFT OUTER JOIN progresses AS pr ON m.id = pr.participant_id
AND (pr.status = 'completed')
WHERE g.program_id = ANY(#1)
GROUP BY g.id
ORDER BY g.program_id, g.position, g.id
SQL
def generate
...
It selects records where g.program_id has a value existing in the array returned by #1 request, which they set as a query parameter (for example SELECT...) somewhere further in the program.
You can equally use SOME(#1) here.
By the way strictly speaking, this isn't a sql query. While there is no sql.execute or something like that, it's just a multiline string assignment.
I'm trying to make a Rails model scope based on the following query:
SELECT * FROM tableA a
INNER JOIN tableB b ON a.id = b.id
WHERE a.id = (SELECT MAX(id) FROM tableB WHERE field = a.field)
I want to join rows of tableA with only one of tableB rows (the max one).
Is it possible?
Thank you!
TableA
.joins(:tableB)
.where("a.id = (SELECT MAX(id) FROM tableB WHERE field = a.field)")
You'll need to have an association between the two tables if you want the joins method works