Dzięki SQL mogę łatwo wykonywać takie pod-zapytania
User.where(:id => Account.where(..).select(:user_id))
To daje:
SELECT * FROM users WHERE id IN (SELECT user_id FROM accounts WHERE ..)
Jak mogę to zrobić za pomocą 3 activerecord / arel / meta_where railsów?
Potrzebuję / chcę prawdziwych podzapytań, żadnych obejść w Ruby (przy użyciu kilku zapytań).
ruby-on-rails-3.1
subquery
arel
gucki
źródło
źródło
not
warunek . Udało mi się to osiągnąć w Railsach 3, dostosowując podejście w tym poście :subquery = Profile.select("user_id").where(gender: 'm')).to_sql; Message.where('user_id NOT IN (#{subquery}))
ZasadniczoActiveRecord
metody są używane do tworzenia kompletnego, odpowiednio cytowanego podzapytania, które jest następnie wstawiane do zewnętrznego zapytania. Głównym minusem jest to, że parametry podzapytania nie są związane.Message.where.not(user_id: Profile.select("user_id").where(gender: 'm'))
taka, że generuje podselekcję "NOT IN". Właśnie rozwiązałem mój problem ...W ARel
where()
metody mogą przyjmować tablice jako argumenty, które wygenerują zapytanie „WHERE id IN ...”. Więc to, co napisałeś, jest w porządku.Na przykład następujący kod ARel:
User.where(:id => Order.where(:user_id => 5)).to_sql
... co jest równoważne z:
User.where(:id => [5, 1, 2, 3]).to_sql
... wyświetli następujący kod SQL w bazie danych PostgreSQL:
SELECT "users".* FROM "users" WHERE "users"."id" IN (5, 1, 2, 3)"
Aktualizacja: w odpowiedzi na komentarze
Okej, więc źle zrozumiałem pytanie. Uważam, że chcesz, aby zapytanie podrzędne wyraźnie wymieniało nazwy kolumn, które mają być wybrane, aby nie trafić do bazy danych dwoma zapytaniami (co w najprostszym przypadku robi ActiveRecord).
Możesz użyć
project
dlaselect
w swoim sub-selekcji:accounts = Account.arel_table User.where(:id => accounts.project(:user_id).where(accounts[:user_id].not_eq(6)))
... co dałoby następujący kod SQL:
SELECT "users".* FROM "users" WHERE "users"."id" IN (SELECT user_id FROM "accounts" WHERE "accounts"."user_id" != 6)
Mam szczerą nadzieję, że tym razem dałem Ci to, czego chciałeś!
źródło
Sam szukałem odpowiedzi na to pytanie i wpadłem na alternatywne podejście. Pomyślałem, że się tym podzielę - mam nadzieję, że to komuś pomoże! :)
# 1. Build you subquery with AREL. subquery = Account.where(...).select(:id) # 2. Use the AREL object in your query by converting it into a SQL string query = User.where("users.account_id IN (#{subquery.to_sql})")
Bingo! Bango!
Działa z Railsami 3.1
źródło
subquery = Account.where(...).select(:id).to_sql
query = User.where("users.account_id IN (#{subquery})")
Inna alternatywa:
Message.where(user: User.joins(:profile).where(profile: { gender: 'm' })
źródło
To jest przykład zagnieżdżonego podzapytania używającego rails ActiveRecord i używających JOINs, gdzie możesz dodać klauzule do każdego zapytania, a także wynik:
Możesz dodać zagnieżdżone zakresy inner_query i external_query w pliku modelu i użyj ...
inner_query = Account.inner_query(params) result = User.outer_query(params).joins("(#{inner_query.to_sql}) alias ON users.id=accounts.id") .group("alias.grouping_var, alias.grouping_var2 ...") .order("...")
Przykład zakresu:
scope :inner_query , -> (ids) { select("...") .joins("left join users on users.id = accounts.id") .where("users.account_id IN (?)", ids) .group("...") }
źródło