How to properly add brackets to SQL queries with 'or' and 'and' clauses by using Arel?
I've successfully used this gem: squeel which comes on top of Arel so you don't have to mess with it. So in order to generate your query you would do something like this in Squeel:
@articles = Article.
where{
( user_id.eq(1) | status.eq('published') ) |
( user_id.in([10, 11, 12, '<...>']) & status.eq('temp') )
}
# since this is an ActiveRecord::Relation we can play around with it
@articles = @articles.select{ [ user_id, status ] }
# and you can also inspect your SQL to see what is going to come out
puts @articles.to_sql
The more complicated your queries get the more you're going to like this gem.
How to control bracket position on ActiveRecord query with `OR` clause
You can get something similar, just without the main parentheses wrapping the where clause conditions:
User.where('foo IS NULL')
.or(User.where('10 > 20'))
.merge(User.where(bar: nil).or(User.where('10 > 1')))
# SELECT "users".*
# FROM "users"
# WHERE ((foo IS NULL) OR (10 > 20)) AND ("users"."bar" IS NULL OR (10 > 1))
Consider foo
equals 1
and bar
equals 2
.
How to dynamic, handle nested WHERE AND/OR queries using Rails and SQL
So, I managed to solve this in about an hour using Arel with rails
dt = SpaceDate.arel_table
pt = SpacePrice.arel_table
combined_clauses = SpacePrice.price_types.map do |price_type, _|
amount_cents = space.send("#{price_type}_price").price_cents
pt[:price_type]
.eq(price_type)
.and(pt[:price_cents].not_eq(amount_cents))
end.reduce(&:or)
space.dates
.joins(:price)
.where(dt[:date].between(start_date..end_date).and(combined_clauses))
end
And the SQL output is:
SELECT "space_dates".* FROM "space_dates"
INNER JOIN "space_prices" ON "space_prices"."space_date_id" = "space_dates"."id"
WHERE "space_dates"."space_id" = $1
AND "space_dates"."date" BETWEEN '2020-06-11' AND '2020-06-15'
AND (
("space_prices"."price_type" = 'hourly'
AND "space_prices"."price_cents" != 9360
OR "space_prices"."price_type" = 'daily'
AND "space_prices"."price_cents" != 66198)
OR "space_prices"."price_type" = 'monthly'
AND "space_prices"."price_cents" != 5500
) LIMIT $2
What I ended up doing was:
- Creating an array of clauses based on the enum key and the
price_cents
- Reduced the clauses and joined them using
or
- Added this to the main query with an
and
operator and thecombined_clauses
Grouping ands and ors in AREL
I believe that according to the operator precedence
The problem is that AND has higher precedence than OR. So 1 AND 2 OR 3
is equivalent to (1 AND 2) OR 3
.
As a side note: if you use a wrapper like this one, you can write:
User.where((User[:created_at] > 3.days.ago) & (User[:enabled] == true))
Cannot programmatically combine AND and OR conditions using Arel
Arel::Nodes::Grouping wraps nodes in parentheses.
Arel::Nodes::Grouping.new(Arel::Nodes::And.new("blah", "there")).to_sql
# => ('blah' AND 'there')
How to use AREL when using multiple databases?
table = Arel::Table.new("db_name.table_name")
Is it possible to generate SQL `OR` clauses by using scope methods?
We have been discussing this on the squeel issue tracker and the basic answer seems to be no. At least not with scopes; but if you convert the scopes into sifters you can.
Related Topics
What's the R Equivalent of SQL's Like 'Description%' Statement
How to Combine Results of Two Queries into a Single Dataset
Connecting to Oracle Database Using SQL Server Integration Services
Ora-00933: SQL Command Not Properly Ended
Using Start Date and End Date in Access Query
How to Create Simple Fuzzy Search with Postgresql Only
Window Functions to Count Distinct Records
SQL Join Two Tables Without Keys/Relations
How to Get List of Values in Group_By Clause
Rodbc SQLsave Table Creation Problems
Stored Procedure Exec VS Sp_Executesql Difference
Insert Data and Set Foreign Keys with Postgres
Normalizing Accented Characters in MySQL Queries
Oracle SQL Syntax: Quoted Identifier
Find Referenced Field(S) of Foreign Key Constraint