2017-10-11 36 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匹配的最長的命名披露。