Rails: Using group() on a joined table column without raw SQL - ruby-on-rails

I have a small problem with grouping an ActiveRecord::Relation. I am trying to group a query by a joined table column without using raw SQL.
The code at the moment looks like that:
Sale::Product.joins(stock_product::supplier).group('core_suppliers.id').first
Result:
Sale::Product Load (42989.5ms) SELECT `sale_products`.* FROM `sale_products` INNER JOIN `stock_products` ON `stock_products`.`deleted_at` IS NULL AND `stock_products`.`id` = `sale_products`.`stock_product_id` INNER JOIN `core_suppliers` ON `core_suppliers`.`id` = `stock_products`.`core_supplier_id` GROUP BY core_suppliers.id ORDER BY `sale_products`.`id` ASC LIMIT 1
I tried to solve this problem by using merge:
Sale::Product.joins(stock_product: :supplier).merge(::Core::Supplier.group(:id)).first
Result:
Sale::Product Load (32428.4ms) SELECT `sale_products`.* FROM `sale_products` INNER JOIN `stock_products` ON `stock_products`.`deleted_at` IS NULL AND `stock_products`.`id` = `sale_products`.`stock_product_id` INNER JOIN `core_suppliers` ON `core_suppliers`.`id` = `stock_products`.`core_supplier_id` GROUP BY `sale_products`.`core_supplier_id` ORDER BY `sale_products`.`id` ASC LIMIT 1
I don't understand why Active::Record doesn't group my association by the column of the merged table. Especially since this way works with ```order()````.
Thanks for your help in advance

You can try Arel library that was introduced in Rails 3 for use in constructing SQL queries.
Just replace ::Core::Supplier.group(core_supplier: :id) to ::Core::Supplier.arel_table[:id] in your code:
Sale::Product.joins(stock_product::supplier).group(::Core::Supplier.arel_table[:id]).first
Update
If you don't want to use Arel directly in your queries you can hide Arel implementation in ApplicationRecord like this:
class ApplicationRecord < ActiveRecord::Base
def self.[](attribute)
arel_table[attribute]
end
end
And than your query can be rewritten like this:
Sale::Product.joins(stock_product::supplier).group(::Core::Supplier[:id]).first

Related

Query on ruby on Rails

How do you query on Ruby on Rails or translate this query on Ruby on Rails?
SELECT
orders.item_total,
orders.total,
payments.created_at,
payments.updated_at
FROM
public.payments,
public.orders,
public.line_items,
public.variants
WHERE
payments.order_id = orders.id AND
orders.id = line_items.order_id AND
This is working on Postgres but I'm new to RoR and it's giving me difficulty on querying this sample.
So far this is what I have.
Order.joins(:payments,:line_items,:variants).where(payments:{order_id: [Order.ids]}, orders:{id:LineItem.orders_id}).distinct.pluck(:email, :id, "payments.created_at", "payments.updated_at")
I have a lot of reference before asking a question here are the links.
How to combine two conditions in a where clause?
Rails PG::UndefinedTable: ERROR: missing FROM-clause entry for table
Rails ActiveRecord: Pluck from multiple tables with same column name
ActiveRecord find and only return selected columns
https://guides.rubyonrails.org/v5.2/active_record_querying.html
from all that link I produced this code that works for testing.
Spree::Order.joins(:payments,:line_items,:variants).where(id: [Spree::Payment.ids]).distinct.pluck(:email, :id)
but when I try to have multiple queries and pluck a specific column name from a different table it gives me an error.
Update
So I'm using Ransack to query I produced this code.
#search = Spree::Order.ransack(
orders_gt: params[:q][:created_at_gt],
orders_lt: params[:q][:created_at_lt],
payments_order_id_in: [Spree::Order.ids],
payments_state_eq: 'completed',
orders_id_in: [Spree::LineItem.all.pluck(:order_id)],
variants_id_in: [Spree::LineItem.ids]
)
#payment_report = #search.result
.includes(:payments, :line_items, :variants)
.joins(:line_items, :payments, :variants).select('payments.response_code, orders.number, payments.number')
I don't have error when I remove the select part and I need to get that specific column. Is there a way?
You just have to make a join between the tables and then select the columns you want
Spree::Order.joins(:payments, :line_items).pluck("spree_orders.total, spree_orders.item_total, spree_payments.created_at, spree_payments.updated_at")
or
Spree::Order.joins(:payments, :line_items).select("spree_orders.total, spree_orders.item_total, spree_payments.created_at, spree_payments.updated_at")
That is equivalent to this query
SELECT spree_orders.total,
spree_orders.item_total,
spree_payments.created_at,
spree_payments.updated_at
FROM "spree_orders"
LEFT OUTER JOIN "spree_payments" ON "spree_payments"."order_id" = "spree_orders"."id"
LEFT OUTER JOIN "spree_line_items" ON "spree_line_items"."order_id" = "spree_orders"."id"
You can use select_all method.This method will return an instance of ActiveRecord::Result class and calling to_hash on this object would return you an array of hashes where each hash indicates a record.
Order.connection.select_all("SELECT
orders.item_total,
orders.total,
payments.created_at,
payments.updated_at
FROM
public.payments,
public.orders,
public.line_items,
public.variants
WHERE
payments.order_id = orders.id AND
orders.id = line_items.order_id").to_hash

SQL not working for pg

I'm trying to use SQL to get information from a Postgres database using Rails.
This is what I've tried:
Select starts_at, ends_at, hours, employee.maxname, workorder.wonum from events where starts_at>'2018-03-14'
inner join employees on events.employee_id = employees.id
inner join workorders on events.workorder_id = workorders.id;
I get the following error:
ERROR: syntax error at or near "inner"
LINE 2: inner join employees on events.employee_id = employees.id
Sami's comment is correct, but since this question is tagged with ruby-on-rails you can try to use ActiveRecord's API to do the same:
Make sure that your models relations are defined
class Event < ActiveRecord::Base
belongs_to :employee
belongs_to :workorder
end
And then you can do something like:
Event
.where('starts_at > ?', '2018-03-14')
.joins(:employee, :workorder)
or
Event
.joins(:employee, :workorder)
.where('starts_at > ?', '2018-03-14')
And you don't need to worry which one goes first.
In general, it's suboptimal to create the SQL queries in rails if you don't absolutely need to because they're harder to maintain.
You request should look at this :
select starts_at, ends_at, hours, employee.maxname, workorder.wonum
from events
inner join employees on events.employee_id = employees.id
inner join workorders on events.workorder_id = workorders.id
where starts_at>'2018-03-14';

Converting Complex SQL Joins into Rails ActiveRecord Query

I have the following SQL which executes correctly in PGAdmin, but try as I might, I cannot get the join working and executing correctly.
SELECT "Reading_ID", "Pixel_ID", "Level", "Readings"."Time"
FROM "PixelReadings"
INNER JOIN "Readings" ON ("Reading_ID" = "Readings"."ID")
INNER JOIN "Sources" ON ("Readings"."Source_ID" = "Sources"."ID")
INNER JOIN location_mappings ON ("Sources"."ID" = "location_mappings"."source_id")
WHERE ("Readings"."Time" BETWEEN '2016-07-25 06:03:07 UTC' AND '2016-07-26 06:03:07 UTC')
AND "location_mappings"."location_id" = 16
AND ("location_mappings"."use_order" = 0)
AND "Pixel_ID" = ("location_mappings"."pixel_y" * 511) + "location_mappings"."pixel_x"
ORDER BY ("Reading_ID") ASC
LIMIT 4;
Can anyone help with converting this into an ActiveRecord Query. I have 2 specific problems, trying to get
.includes(readings: {sources: :location_mappings})
And how to correctly format the 'where' methods. Any help would be greatly appreciated.
"Reading_ID", "Pixel_ID", "Level" are all the columns in the PixelReadings schema - I need all the PixelReading columns plus the Time column from the Readings table.
Are the table names are correctly set on your rails models? I see that the table names are not in correspondence with the rails conventions. If you haven't set them, you can set em like this (do the same for all models that doesn't have table names different from rails conventions):
class PixelReading < ActiveRecord::Base
self.table_name = "PixelReadings"
end
Your query would look something like this
PrixelReading
.joins(readings: {sources: :location_mappings})
.where("Readings.Time BETWEEN '2016-07-25 06:03:07 UTC' AND '2016-07-26 06:03:07 UTC'")
.where(location_mappings: { location_id: 16, use_order: 0 })
.where("Pixel_ID = (location_mappings.pixel_y * 511) + location_mappings.pixel_x")
.order("PixelReadings.Reading_ID ASC")
.select("PixelReadings.*, Readings.Time")
.limit(4)

How to write query in active record to select from two or more tables in rails 3

I don't want to use join
I want to manually compare any field with other table field
for example
SELECT u.user_id, t.task_id
FROM tasks t, users u
WHERE u.user_id = t.user_id
how can i write this query in Rails ??
Assuming you have associations in your models, you can simply do as follow
User.joins(:tasks).select('users.user_id, tasks.task_id')
you can also do as follow
User.includes(:tasks).where("user.id =tasks.user_id")
includes will do eager loading check the example below or read eager loading at here
users = User.limit(10)
users.each do |user|
puts user.address.postcode
end
This will run 11 queries, it is called N+1 query problem(first you query to get all the rows then you query on each row again to do something). with includes Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries.
Now when you do;
users = User.includes(:address).limit(10)
user.each do |user|
puts user.address.postcode
end
It will generate just 2 queries as follow
SELECT * FROM users LIMIT 10
SELECT addresses.* FROM addresses
WHERE (addresses.user_id IN (1,2,3,4,5,6,7,8,9,10))
Plus if you don't have associations then read below;
you should be have to look at http://guides.rubyonrails.org/association_basics.html
Assuming your are trying to do inner join, by default in rails when we associate two models and then query on them then we are doing inner join on those tables.
You have to create associations between the models example is given below
class User
has_many :reservations
...# your code
end
And in reservations
class Reservations
belongs_to :user
... #your code
end
Now when you do
User.joins(:reservations)
the generated query would look like as follow
"SELECT `users`.* FROM `users` INNER JOIN `reservations` ON `reservations`.`user_id` = `users`.`id`"
you can check the query by doing User.joins(:reservations).to_sql in terminal
Hopefully it would answer your question
User.find_by_sql("YOUR SQL QUERY HERE")
You can use as follows..
User.includes(:tasks).where("user.id =tasks.user_id").order(:user.id)

How do I get Rails ActiveRecord to generate optimized SQL?

Let's say that I have 4 models which are related in the following ways:
Schedule has foreign key to Project
Schedule has foreign key to User
Project has foreign key to Client
In my Schedule#index view I want the most optimized SQL so that I can display links to the Schedule's associated Project, Client, and User. So, I should not pull all of the columns for the Project, Client, and User; only their IDs and Name.
If I were to manually write the SQL it might look like this:
select
s.id,
s.schedule_name,
s.schedule_type,
s.project_id,
p.name project_name,
p.client_id client_id,
c.name client_name,
s.user_id,
u.login user_login,
s.created_at,
s.updated_at,
s.data_count
from
Users u inner join
Clients c inner join
Schedules s inner join
Projects p
on p.id = s.project_id
on c.id = p.client_id
on u.id = s.user_id
order by
s.created_at desc
My question is: What would the ActiveRecord code look like to get Rails 3 to generate that SQL? For example, somthing like:
#schedules = Schedule. # ?
I already have the associations setup in the models (i.e. has_many / belongs_to).
I think this will build (or at least help) you get what you're looking for:
Schedule.select("schedules.id, schedules.schedule_name, projects.name as project_name").joins(:user, :project=>:client).order("schedules.created_at DESC")
should yield:
SELECT schedules.id, schedules.schedule_name, projects.name as project_name FROM `schedules` INNER JOIN `users` ON `users`.`id` = `schedules`.`user_id` INNER JOIN `projects` ON `projects`.`id` = `schedules`.`project_id` INNER JOIN `clients` ON `clients`.`id` = `projects`.`client_id`
The main problem I see in your approach is that you're looking for schedule objects but basing your initial "FROM" clause on "User" and your associations given are also on Schedule, so I built this solution based on the plain assumption that you want schedules!
I also didn't include all of your selects to save some typing, but you get the idea. You will simply have to add each one qualified with its full table name.

Resources