[编辑 2]
鉴于 articles_per_week 不是一个字段而是一个方法,您将不得不放弃作用域可链接性 - 归结起来的作用域只是生成 SQL 语句的 AREL,没有办法桥接实例方法和数据库之间的差距。在这一点上,事情实际上变得更容易了,你可以把整个事情变成一个类方法,最后调用 #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');