2017-10-11 37 views
3

我有一个Company模型,有很多DisclosuresDisclosure具有名为title,pdfpdf_sha256的列。如何使列查询唯一的列

class Company < ActiveRecord::Base 
    has_many :disclosures 
end 

class Disclosure < ActiveRecord::Base 
    belongs_to :company 
end 

我想让它独特的pdf_sha256如果pdf_sha256nil应该被视为是唯一的。

如果是Array,我会这样写。

companies_with_sha256 = company.disclosures.where.not(pdf_sha256: nil).group_by(&:pdf_sha256).map do |key,values| 
    values.max_by{|v| v.title.length} 
end 
companies_without_sha256 = company.disclosures.where(pdf_sha256: nil) 
companies = companies_with_sha256 + companeis_without_sha256 

如何通过使用Rails查询来获得相同的结果?

回答

0

有可能通过先取不同id每个不同pdf_sha256作为一个子查询做到这一点在一个查询,然后在查询中通过将子查询作为获得该集ID中的元素如下:

def unique_disclosures_by_pdf_sha256(company) 
    subquery = company.disclosures.select('MIN(id) as id').group(:pdf_sha256) 
    company.disclosures.where(id: subquery) 
    .or(company.disclosures.where(pdf_sha256: nil)) 
end 

关于这个伟大的事情是,ActiveRecord的是延迟加载,所以第一个subquery不会运行并将合并到第二个主要查询中以在数据库中创建单个查询。然后它将检索pdf_sha256所有唯一的disclosures加上所有pdf_sha256设置为nil的那些。

如果你很好奇,给予公司,得到的查询将是这样的:

SELECT "disclosures".* FROM "disclosures" 
WHERE (
    "disclosures"."company_id" = $1 AND "disclosures"."id" IN (
    SELECT MAX(id) as id FROM "disclosures" WHERE "disclosures"."company_id" = $2 GROUP BY "disclosures"."pdf_sha256" 
) 
    OR "disclosures"."company_id" = $3 AND "disclosures"."pdf_sha256" IS NULL 
) 

关于这个解决方案的最大好处是,返回的值是一个ActiveRecord的查询,所以不会被加载直到你真正需要。您也可以使用它来保持链式查询。例如,您可以只选择id而不是整个模型和限制由数据库返回的结果数:

unique_disclosures_by_pdf_sha256(company).select(:id).limit(10).each { |d| puts d } 
0
Model.select(:rating) 

这是一个Model对象的数组。不是简单的评级。从uniq的角度来看,它们完全不同。您可以使用此:

Model.select(:rating).map(&:rating).uniq 

或本(最有效)

Model.uniq.pluck(:rating) 

Model.distinct.pluck(:rating) 

更新

显然,铁轨5.0.0.1的,它仅适用于 “顶级” 的查询,如以上。不适用于集合代理(例如,“has_many”关系)。

Address.distinct.pluck(:city) # => ['Moscow'] 
user.addresses.distinct.pluck(:city) # => ['Moscow', 'Moscow', 'Moscow'] 

在这种情况下,重复数据删除查询后

user.addresses.pluck(:city).uniq # => ['Moscow'] 
+0

ok @SebastiánPalma – nishant

+0

在你的例子中,我想获得所有列的地址,而不仅仅是城市列。 – ironsand

0

为什么不:

ids = Disclosure.select(:id, :pdf_sha256).distinct.map(&:id) 
Disclosure.find(ids) 

的ID门槛是截然不同的两种方式,因为它是主键,所以你必须do是映射id并通过id找到Disclosures。

+0

我想通过'pdf_sha256'的唯一性来获取'披露'模型过滤器的所有列。 – ironsand

0

您可以通过使用uniq的方法

Company.first.disclosures.to_a.uniq(&:pdf_sha256) 

实现这一目标这将返回该公开的内容cloumn “pdf_sha256”

希望记录的uniq这可以帮助你!干杯

+0

'ActiveRecord :: QueryMethods#uniq'只是'ActiveRecord :: QueryMethods#distinct'的别名。因此该方法在这种情况下不起作用。我想你混淆了'Array#uniq'。您的代码可以使用'to_a'方法,如'company.disclosures.to_a.uniq(&:pdf_sha256)'。 – ironsand

+0

谢谢@ironsand。我编辑了我的答案。希望这可以帮助你。 –

0

如果你需要用不同的pdf_sha256,在那里你需要没有明确条件的关系,你可以使用group为 -

scope :unique_pdf_sha256, -> { where.not(pdf_sha256: nil).group(:pdf_sha256) } 
scope :nil_pdf_sha256, -> { where(pdf_sha256: nil) } 

您也可以使用or,但传递给它的关系必须结构兼容。因此,即使您在这两个示波器中获得相同类型的关系,也不能在or中使用它。

编辑:为了使结构互相可以看到兼容@AlexSantos的answer

1

假设你正在使用Rails 5,你可以链。或者命令合并这两个查询。

pdf_sha256_unique_disclosures = company.disclosures.where(pdf_sha256: nil).or(company.disclosures.where.not(pdf_sha256: nil)) 

然后你可以继续你的group_by逻辑。

但是,在上面的示例中,我不完全确定目标是什么,但我很好奇如何更好地理解如何使用生成的companies变量。

如果你想有独特pdf_sha256键的哈希值,包括零,而其产生的独特的信息披露文件,你可以尝试以下方法:

sorted_disclosures = company.disclosures.group_by(&:pdf_sha256).each_with_object({}) do |entries, hash| 
    hash[entries[0]] = entries[1].max_by{|v| v.title.length} 
end 

这应该给你类似GROUP_BY一个结果哈希状结构您的密钥都是您的唯一pdf_sha256,并且该值将是与pdf_sha256匹配的最长的命名披露。