write query in ruby from SQL query - ruby-on-rails

How do I write the below query in ruby.
select count(*), project_types.project_type_name,company_id from project_type_stages_questions,project_types where (project_type_stage_id in
(select project_type_stages_id from project_type_stages where project_type_id in (select project_type_id from project_types))) group by project_type_stage_id,project_types.project_type_name,company_id

If you have a complex sql to do, and you're going to use its result, you can pass it to the db manager:
sql = "select count(*), project_types.project_type_name,company_id from project_type_stages_questions,project_types where (project_type_stage_id in (select project_type_stages_id from project_type_stages where project_type_id in (select project_type_id from project_types))) group by project_type_stage_id,project_types.project_type_name,company_id"
ActiveRecord::Base.connection.execute(sql)
The public api don't provide much information, but it's here.
If you need an ActiveRecord::Relation as result, them I suggest to use Arel.

Related

Converting a SQL statement to rails command

I have a situation where I need to fetch only few records from a particular active record query response.
#annotations = Annotations.select('*', ROW_NUMBER() OVER (PARTITION BY column_a) ORDER BY column_b)
Above is the query for which the #annotations is the Active Record Response on which I would like to apply the below logic. Is there a better way to write the below logic in rails way?
with some_table as
(
select *, row_number() over (partition by column_a order by column_b) rn
from the_table
)
select * from some_table
where (column_a = 'ABC' and rn <= 10) or (column_b <> 'AAA')
ActiveRecord does not provide CTEs in its high level API; however with a little Arel we can make this a sub query in the FROM clause
annotations_table = Annotation.arel_table
sub_query = annotations_table.project(
Arel.star,
Arel.sql('row_number() over (partition by column_a order by column_b)').as(:rn)
)
query = Arel::Nodes::As.new(sub_query, Arel.sql(annotations_table.name))
Annotation.from(query).where(
annotations_table[:column_a].eq('ABC').and(
annotations_table[:rn].lt(10)
).or(annotations_table[:column_b].not_eq('AAA'))
)
The result will be a collection of Annotation objects using your CTE and the filters you described.
SQL:
select annotations.*
from (
select *, row_number() over (partition by column_a order by column_b) AS rn
from annotations
) AS annotations
where (annotations.column_a = 'ABC' and annotations.rn <= 10) or (annotations.column_b <> 'AAA')
Notes:
With a little extra work we could make this a CTE but it does not seem needed in this case
We could use a bunch of hacks to transition this row_number() over (partition by column_a order by column_b) to Arel as well but it did not seem pertinent to the question.

Nested queries. Select as column

Is it possible to write this query in ROR?
SELECT column_1,
(SELECT name FROM table_2 WHERE table_2.column_1 = table_1.column_1) as name
FROM table_1;
Yes, it is possible:
Table_1.select("column_1, (SELECT name FROM table_2 WHERE table_2.column_1 = table_1.column_1) as name")
If you will user Arel it will seems yet more complicated then this.
But exists other ways to simplify this query:
split it to two query and merge it together in Rails
using joins method for join table_1 and table_2 and select field table_2.name.

Rails inner join two sql statements to get ActiveRecord::Relation

I have complex query that I want to join itself to do additional computations. In sql I can do
SELECT t1.*, t2.*
FROM (SQL) AS t1
INNER JOIN
(SQL) AS t2
ON t1.num = t2.num - 1
Suppose the SQL is the query I want to join.
How to do that in rails with ActiveRecord / arel (or something else) to get ActiveRecord::Relation and to be able to use where() on it.
If I do it with sql and execute/select_all I get PG:result or hash and can't use where memthod anymore.
First solution is interpolating other query using to_sql method:
Place.select("places.*, communes.*").joins("INNER JOIN (#{
Commune.where(:id => [1,2,3]).to_sql
}) as communes ON communes.id = places.commune_id")
Second solution is using Arel's merge method:
Place.select("places.*, communes.*")
.joins(:commune)
.merge(Commune.where(:id => [1,2,3]))

ActiveRecord subquery in select clause

So I'm getting a bunch of Volunteers records, with some filtering and sorting, which is fine. But I'd like to also get a count of the number of Children each volunteer is helping (using volunteer_id on children table), as a sub-query in the select clause to avoid having to perform a separate query for each record. As a bonus it would be good to be able to sort by this count too!
I'd like to end up with a generated query like this and be able to access the 'kids' column:
SELECT id, name, (SELECT COUNT(*) FROM children WHERE volunteer_id = volunteers.id) AS kids FROM volunteers
Is there any way of doing this with Arel? I've had a bit of a scout around and haven't found anything yet.
Alternatively, is it possible to join to the children table and get: count(children.id) ?
Thanks for any help :)
The proper way of doing this with SQL is with a GROUP BY clause:
SELECT v.id, v.name, COUNT(*) AS kids
FROM volunteers v
LEFT OUTER JOIN children c ON v.id = c.volunteer_id
GROUP BY v.id, v.name
There is a method .group() in AR for using GROUP BY queries.

DB2 Update with join queries

I am using DB2 for performing the below update operation.
update DATA set B_DESC=P_DESC, P_DESC=null
where B_DESC= *, P_DESC=*
(Select B_DESC,P_DESC from C_DATA)
The below is actually possible but since complex joins are involved in that sub query it is not advisable to use like below
update DATA set B_DESC=P_DESC, P_DESC=null
where B_DESC= (Select B_DESC from C_DATA), P_DESC=(Select P_DESC from C_DATA)
I have to update DATA table, but the B_DESC and P_DESC i have to fetch it from C_DATA table and use it in the UPDATE query.
Please let me know how to do it. It has to be a single query if possible.
Thanks in advance.
Use a merge query to update the table, instead of join. DB2 does not accept join in update query for that purpose, you have to use a merge:
MERGE INTO TABLE_NAME1 A
USING (SELECT COL1, COL2 FROM TABLE_NAME2) B
ON A.COL1 = B.COL2
WHEN MATCHED AND A.COL1 = B.COL2
THEN UPDATE SET A.COL1 = B.COL2;
Does your 1st query not work? I'm not familiar with comma-separating parts of the WHERE clause (it's not valid on my version of DB2 - is it actually part of the syntax?).
Normally, when I need to run these types of update queries, I use an EXISTS clause, like this:
UPDATE data as a SET b_desc = p_desc, p_desc = null
WHERE EXISTS (SELECT '1'
FROM c_data as b
WHERE b.b_desc = a.b_desc
AND b.p_desc = a.p_desc)

Resources