[Редактировать 2]
Учитывая, что Articles_per_week это не поле, а метод, вам придется отказаться от возможности цепочки областей видимости - области видимости сводятсяпросто AREL, который генерирует оператор SQL, и нет никакого способа преодолеть разрыв между методом экземпляра и базой данных.На этом этапе все становится проще, и вы можете просто сделать все это классовым методом с вызовом # reject в конце, чтобы отбросить те, которые не соответствуют критериям подсчета:
class Batch < ActiveRecord::Base
belongs_to :project
has_many :articles
def self.all_completed
joins(:articles).
includes(:project).
where("articles.status = 'Completed'").
group("batches.id").
select("batches.*, count(articles.id) as article_count").
reject {|batch| batch.article_count.to_i < batch.project.articles_per_week}
end
end
И вы по-прежнему получаете минимальные удары по БД с этим (2).Но, как уже было отмечено, вызов #reject преобразует ваш вывод в базовый массив объектов, а не в цепочку ActiveRecord с цепочкой.
Не уверен, почему мне пришлось преобразовать количество статей в целое число, но так оно и было.
Я оставил свои предыдущие версии, так как я думаю, что они предоставляют хорошую дополнительную информацию для других сценариев.
[Редактировать 1]
Вот версия, которую вы можете добавить в качестве области действия для Batch, чтобы она была цепной для других областей.
scope :all_completed,
joins(:articles, :project).
where("articles.status = 'Completed'").
having("count(articles.id) >= projects.articles_per_week").
group("batches.id").
select("batches.*, projects.articles_per_week")
Я заметил, что при тестировании это может оказать влияние на некоторыестандартных методов ActiveRecord.Например, #size не работает, потому что в ActiveRecord, который переведен в SELECT COUNT (*), и он не будет работать здесь из-за отсутствия projects.articles_per_week в выбранных полях.
[Оригинал]
Мой первый ответ здесь.
Я не уверен, соответствует ли это вашему запросу "просто", но он выполняет задачу в одном операторе SQL.
Article.includes([:batch => :project]).where(:status => 'Completed').group(:batch_id).having("count(articles.id) >= projects.articles_per_week").map(&:batch)
Это предполагает Rails 3 и следующие настройки модели:
class Batch < ActiveRecord::Base
has_many :articles
belongs_to :project
end
class Project < ActiveRecord::Base
has_many :batches
end
class Article < ActiveRecord::Base
belongs_to :batch
end
Кстати, вот мои тестовые данные.Структура выше правильно вытащила партии 1 и 2 и проигнорировала партию 3.
-- phpMyAdmin SQL Dump
-- version 3.3.2deb1
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Jun 27, 2011 at 07:13 PM
-- Server version: 5.1.41
-- PHP Version: 5.3.2-1ubuntu4.9
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
--
-- Database: `stack_development`
--
-- --------------------------------------------------------
--
-- Table structure for table `articles`
--
DROP TABLE IF EXISTS `articles`;
CREATE TABLE IF NOT EXISTS `articles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`batch_id` int(11) DEFAULT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`status` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=16 ;
--
-- Dumping data for table `articles`
--
INSERT INTO `articles` (`id`, `batch_id`, `name`, `status`, `created_at`, `updated_at`) VALUES
(1, 1, 'Test 1 Article', 'Completed', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(2, 1, 'Test 2 Article', 'Completed', '2011-06-27 22:40:36', '2011-06-27 22:40:36'),
(3, 1, 'Test 3 Article', 'Completed', '2011-06-27 22:40:42', '2011-06-27 22:40:42'),
(4, 1, 'Test Article 4', 'Completed', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(5, 1, 'Test Article 5', 'Pending', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(6, 2, 'Test 1 Article', 'Completed', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(7, 2, 'Test 2 Article', 'Completed', '2011-06-27 22:40:36', '2011-06-27 22:40:36'),
(8, 2, 'Test 3 Article', 'Completed', '2011-06-27 22:40:42', '2011-06-27 22:40:42'),
(9, 2, 'Test Article 4', 'Pending', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(10, 2, 'Test Article 5', 'Pending', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(11, 3, 'Test 1 Article', 'Completed', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(12, 3, 'Test 2 Article', 'Completed', '2011-06-27 22:40:36', '2011-06-27 22:40:36'),
(13, 3, 'Test 3 Article', 'Pending', '2011-06-27 22:40:42', '2011-06-27 22:40:42'),
(14, 3, 'Test Article 4', 'Pending', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(15, 3, 'Test Article 5', 'Pending', '2011-06-27 22:38:55', '2011-06-27 22:38:55');
-- --------------------------------------------------------
--
-- Table structure for table `batches`
--
DROP TABLE IF EXISTS `batches`;
CREATE TABLE IF NOT EXISTS `batches` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`project_id` int(11) DEFAULT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=4 ;
--
-- Dumping data for table `batches`
--
INSERT INTO `batches` (`id`, `project_id`, `name`, `created_at`, `updated_at`) VALUES
(1, 1, 'Test 1 Batch', '2011-06-27 22:38:11', '2011-06-27 22:38:11'),
(2, 2, 'Test 2 Batch', '2011-06-27 22:38:11', '2011-06-27 22:38:11'),
(3, 3, 'Test 3 Batch', '2011-06-27 22:38:11', '2011-06-27 22:38:11');
-- --------------------------------------------------------
--
-- Table structure for table `projects`
--
DROP TABLE IF EXISTS `projects`;
CREATE TABLE IF NOT EXISTS `projects` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`articles_per_week` int(11) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=4 ;
--
-- Dumping data for table `projects`
--
INSERT INTO `projects` (`id`, `name`, `articles_per_week`, `created_at`, `updated_at`) VALUES
(1, 'Test 1 project', 3, '2011-06-27 22:37:11', '2011-06-27 22:37:11'),
(2, 'Test 2 Project', 3, '2011-06-27 22:37:21', '2011-06-27 22:37:21'),
(3, 'Test 3 Project', 3, '2011-06-27 22:37:21', '2011-06-27 22:37:21');