Как я могу объединить COUNT (*) для двух разных отношений ActiveRecord в один SQL-запрос? - PullRequest
0 голосов
/ 22 января 2019

С двумя различными объектами отношений ActiveRecord, есть ли способ выполнить один SQL-запрос для сравнения количества отношений?

например. скажем, у меня есть два объекта ActiveRecord :: Relation, как это:

posts = Post.where().where().some_scope
users = User.where().some_other_scope.where().joins(:something)

Чтобы сравнить счетчики каждого отношения, мне нужно выполнить два SQL-запроса.

posts.count == users.count
# => SELECT COUNT(*) FROM posts WHERE... ;
# => SELECT COUNT(*) FROM users INNER JOIN ... WHERE... ;

Я хочу иметь возможность выдавать только один запрос. Что-то вроде:

Post.select("COUNT(first) == COUNT(second) as are_equal"), posts, users).are_equal

Ответы [ 3 ]

0 голосов
/ 22 января 2019

Невозможно объединить два подсчета для двух разных таблиц в один запрос, если вы не используете UNION. Который запустит два отдельных запроса и объединит результаты. Это займет примерно столько же времени, сколько и два запроса по отдельности, за исключением того, что вы заходите на db-сервер только один раз (1 запрос), но вы теряете удобочитаемость. Имхо, мне действительно интересно, стоит ли это того.

например. в одном случае вы можете написать

 if posts.count == users.count 

В другом случае можно написать:

 count_sql = <<-SQL 
    select "Posts" as count_type, count(*) from posts where ... 
    union 
    select "Users" as count_type, count(*) from users where ...
 SQL 

 result = Post.connection.execute(count_sql) 
 if result[0]["count"] == result[1]["count"] 

Вам придется решить, будут ли улучшения производительности приводить к потере читабельности.

0 голосов
/ 22 января 2019

Это невозможно с помощью методов запросов ActiveRecord, но базовый построитель запросов Arel (который использует ActiveRecord внутри) может достичь этого, он выглядит немного менее элегантно:

posts = Post.where().where().some_scope
users = User.where().some_other_scope.where().joins(:something)

posts_table = Post.arel_table
users_table = User.arel_table

posts_count = Arel::Nodes::Count.new([posts_table[:id]]).as('count')
users_count = Arel::Nodes::Count.new([users_table[:id]]).as('count')

union = posts.select(posts_count).arel.union(users.select(users_count).arel)

post_count, user_count = Post.from(posts_table.create_table_alias(union, :posts)).map(&:count)

Хотя в данном случае это может быть не очень полезно (как обсуждалось в других ответах), стоит помнить об Arel, потому что бывают моменты, когда это полезно - я всегда стараюсь избегать необработанного SQL в моих приложениях на Rails, а Arel делает это возможно.

Отличное введение можно найти здесь: https://danshultz.github.io/talks/mastering_activerecord_arel/#/

0 голосов
/ 22 января 2019

Вы всегда можете написать свой собственный запрос SQL.

Допустим, у вас есть две модели, AdminUser и Company.Один из способов сделать то, что вам нужно, будет следующим:

ActiveRecord::Base.connection.execute("SELECT COUNT(*) as nb from admin_users UNION SELECT COUNT(*) as nb from companies;").to_a

В итоге вы получите массив из двух хешей, каждый из которых содержит количество записей в каждой таблице базы данных.

...