How to do where exists in Arel
Here you go:
suppliers= Supplier.arel_table
orders= Order.arel_table
suppliers_with_orders = Supplier.where(
Order.where(orders[:supplier_id]
.eq(suppliers[:id])).exists).to_sql =>
"SELECT `suppliers`.* FROM `suppliers`
WHERE (EXISTS (SELECT `orders`.*
FROM `orders`
WHERE `suppliers`.`id` = `orders`.`supplier_id`))"
Though, an inner join would do this in a more simple - and eventually less performant - way :
Supplier.joins :orders
Rails 3: Arel for NOT EXISTS?
Here is the answer, with strange names because I don't know how to give names for a domain that is for me unknown.
deals = Deal.arel_table
reward_deals = RewardDeal.arel_table
awarding_condition= reward_deals[:awarding_type].eq('deal')\
.and(reward_deals[:deal_id]\
.eq(reward_deals[:awarding_id]))
reward_deals_condition= reward_deals[:deal_id].eq(deals[:id])\
.and(awarding_condition.not)
Deal.where(RewardDeal.where(reward_deals_condition).exists.not)
Rails ActiveRecord WHERE EXISTS query
RAILS 5/6 EDIT: As of https://github.com/rails/rails/pull/29619, Rails started discouraging the direct .exists
call from my original answer. I've updated it to use the new syntax, which invokes the arel
proxy first (.arel.exists
). Also, as of Rails 5, hash conditions work just fine within the EXISTS clause.
With all that taken into consideration, the pure ActiveRecord approach is now:
Trip.where("trips.title LIKE ?", "%Thailand%")
.where( Place.where('trip_id = trips.id AND name LIKE ?', '%Bangkok%').arel.exists )
.where( Place.where('trip_id = trips.id AND name LIKE ?', '%Phuket%').arel.exists )
If that looks a little scary, you do have some other options:
- You could just use
.where('EXISTS(SELECT 1 FROM places WHERE trip_id = trips.id AND name LIKE ?', '%Bangkok%')
, embedding the SQL into your application directly. It's not as hip or cool, but in the long run it may be more maintainable -- it's very unlikely to be deprecated or stop working with future versions of rails. - Pick a gem, like activerecord_where_assoc, which makes the syntax cleaner and may save you from making simple mistakes (like not scoping your EXISTS query correctly, as my original answer did).
Rails: Convert a complex SQL query to Arel or ActiveRecord
While we can certainly build your query in Arel, after reviewing your SQL a bit it looks like it would actually be much cleaner to simply build this using the AR API instead.
The following should produce the exact query you are looking for (sans "newsletter_stories"."author_id" = "group_members"."id"
because this is already implied by the join)
class Newsletter::Author < Application Record
belongs_to :newsletter, inverse_of: :authors
belongs_to :group_member, class_name: "GroupMember", inverse_of: :authorships
scope :without_submissions_for, ->(newsletter_id) {
group_members = GroupMember
.select(:id)
.joins(:stories)
.where(newsletter_stories: {status: 'draft'})
.where.not(
Newsletter::Story
.select(1)
.where(status: 'submitted')
.where(Newsletter::Story.arel_table[:author_id].eq(GroupMember.arel_table[:id]))
.arel.exists
).distinct
where(discarded_at: nil, newsletter_id: newsletter_id, group_member_id: group_members)
}
end
How to write an SQL NOT EXISTS query/scope in the Rails way?
This isn't actually a query that you can build with the ActiveRecord Query Interface alone. It can be done with a light sprinkling of Arel though:
class ProxyConfig < ApplicationRecord
def self.current_versions
pc = arel_table.alias("pc")
where(
unscoped.select(1)
.where(pc[:environment].eq(arel_table[:environment]))
.where(pc[:proxy_id].eq(arel_table[:proxy_id]))
.where(pc[:version].gt(arel_table[:version]))
.from(pc)
.arel.exists.not
)
end
end
The generated SQL isn't identical but I think it should be functionally equivilent.
SELECT "proxy_configs".* FROM "proxy_configs"
WHERE NOT (
EXISTS (
SELECT 1 FROM "proxy_configs" "pc"
WHERE "pc"."environment" = "proxy_configs"."environment"
AND "pc"."proxy_id" = "proxy_configs"."proxy_id"
AND "pc"."version" > "proxy_configs"."version"
)
)
Rails3 Arel Building the Where Clause
What do you mean not execute? where
only return a Relation object, you have to call all
to get the result:
rec.all
How do you create and use functions in Arel that are not bound to a column?
You can use Arel::Nodes::NamedFunction
.
This class initializes with the name of the function and an arguments array. The Vistor for a NamedFunction will assemble the SQL as FUNCTION_NAME(comma, separated, arguments)
.
Since NOW
has no arguments all we need to do is pass an empty arguments array.
fn = Arel::Nodes::NamedFunction.new("NOW",[])
memberships = Arel::Table.new("memberships")
memberships.project(Arel.star).where(
memberships[:expires].lt(fn)
).to_sql
#=> "SELECT * FROM [memberships] WHERE [memberships].[expires] < NOW()"
Function
appears to be a super class for inheritance purposes only, children include Sum
,Exists
,Min
,Max
,Avg
, and NamedFunction
(which allows you to call any other function by name as shown above)
Related Topics
How to Execute a Stored Procedure Inside a Select Query
Replacing Null and Empty String Within Select Statement
Querying Active Directory from SQL Server 2005
Select Random Row from a Postgresql Table with Weighted Row Probabilities
Is There an Oracle Equivalent to SQL Server's Output Inserted.*
Why am I Getting a "[Sql0802] Data Conversion of Data Mapping Error" Exception
How to See Cakephp's SQL Dump in the Controller
Update Int Column in Table with Unique Incrementing Values
Update One of 2 Duplicates in an SQL Server Database Table
Is a Primary Key Necessary in SQL Server
How to Return Default Value from SQL Query
Set Limit for a Table Rows in SQL
Tsql Select into Temp Table from Dynamic SQL
H2 Database Column Name "Group" Is a Reserved Word
How to Get the Actual Stored Procedure Line Number from an Error Message