你不能做一个简单的.group(:name)
,因为这会产生在你的SQL一个GROUP BY name
时,你会选择拆散然后unaggregate d列,留下模糊性挑哪一行和PostgreSQL (rightly IMHO) complains:
When GROUP BY is present, it is not valid for the SELECT list expressions to refer to ungrouped columns except within aggregate functions, since there would be more than one possible value to return for an ungrouped column.
如果你开始添加更多的列到您的分组像这样的东西:
T.group(T.columns.collect(&:name))
然后你会被进行分组你不想要的东西,你最终会拉出整个桌子,这不是你想要的。如果您尝试进行聚合以避免出现分组问题,那么您最终会混合不同的行(即一列将来自一行,而另一列将来自其他行),但这不是您想要的。
ActiveRecord确实不是为这种事情而建的,但你可以在一定的努力下将其屈服于你的意愿。
您正在使用AR,因此您大概有一个id
列。如果你有PostgreSQL 8。4或更高,那么你可以使用window functions作为一种本地化的GROUP BY;你需要窗口两次:一次找出name
/thedate
对,并再次挑选一个id
(以防万一你有多个行有相同的name
和thedate
匹配最早的thedate
),因此得到一个独特的行:
select your_table.*
from your_table
where id in (
-- You don't need DISTINCT here as the IN will take care of collapsing duplicates.
select min(yt.id) over (partition by yt.name)
from (
select distinct name, min(thedate) over (partition by name) as thedate
from your_table
) as dt
join your_table as yt
on yt.name = dt.name and yt.thedate = dt.thedate
)
然后包裹在一个find_by_sql
,你有你的对象。
如果您在共享数据库(或其他没有8.4或更高版本的环境)中使用Heroku,那么您会遇到PostgreSQL 8.3,并且您将不具有窗口功能。在这种情况下,你可能想筛选出在Ruby中,土地的重复:
with_dups = YourTable.find_by_sql(%Q{
select yt.*
from your_table yt
join (select name, min(thedate) as thedate from your_table group by name) as dt
on yt.name = dt.name and yt.thedate = dt.thedate
});
# Clear out the duplicates, sorting by id ensures consistent results
unique_matches = with_dups.sort_by(&:id).group_by(&:name).map { |x| x.last.first }
如果你敢肯定不会有重复的name
/min(thedate)
对那么8.3兼容的解决方案可能做你最好的选择;但是,如果会有大量重复项,那么您希望数据库尽可能地做大量的工作,以避免创建数千个您将要丢弃的AR对象。
也许有比我更强大的PostgreSQL-Fu的其他人会来,并提供更好的东西。
如果'name,min(date)'对在表中出现两次,这就有唯一性问题。 –