2014-02-28 85 views
0

在我有以下型號的應用程序:瞭解查詢(包括聯接,條件)

class Activity < ActiveRecord::Base 
    has_many :activity_items, dependent: :destroy 
    has_many :children, through: :activity_items, source: :activity_itemable, source_type: 'Child' 
    has_many :parents, through: :activity_items, source: :activity_itemable, source_type: 'Parent' 

... 
end 

ActivityItem用於參與者添加到活動

class ActivityItem < ActiveRecord::Base 
    belongs_to :activity_itemable, polymorphic: true 
    belongs_to :activity 
    has_many :children, through: :activity_itemable, source_type: 'Child' 
    has_many :parents, through: :activity_itemable, source_type: 'Parent' 
... 
end  

的活動有一個日期列:'activity_date',以及其他在此不重要的列。
ActityItem具有state,activity_id,activity_itemable_type和activity_itemable_id列。

我已經建立計數參與者「統計」報告(許多家長是如何在活動列表中,許多不同的家長如何存在等)

我已經建立查詢來算,但我有麻煩了解生成的SQL我需要幫助在這裏確定我正在做好查詢。

下面是我的一些疑問,我的問題:

1 - 我想我算多少不同的孩子(有型兒童activity_items)在活動,但只有在比2015年12月31日較舊的活動

Activity.where("activity_date > ?", "2015-12-31").includes(:activity_items).where("activity_items.activity_itemable_type = ?", "Child").references(:activity_items).distinct.count("activity_items.activity_itemable_id") 

SQL:

SELECT DISTINCT COUNT(DISTINCT activity_items.activity_itemable_id) 
FROM "activities" LEFT OUTER JOIN 
    "activity_items" 
    ON "activity_items"."activity_id" = "activities"."id" 
WHERE (activity_date > '2015-12-31') AND (activity_items.activity_itemable_type = 'Child') 

因爲有上兩個不同的表的兩個條件,是不是更適合於使用一個內連接只選擇行毫安考慮這兩個條件?

2 - 相同的查詢,但我從另一個表

ActivityItem.includes(:activity).where('activity_date >= ?', "2015-01-01").where(activity_itemable_type: "Child").distinct.count(:activity_itemable_id) 

同樣的結果開始,但很少有奇怪的事情。 I'dont必須寫在where子句像查詢1.我第一次嘗試寫:

.where('activity.activity_date >= ?', "2015-01-01") 

但是它沒有我必須使用:

.where('activity_date >= ?', "2015-01-01") 

最後這有什麼奇怪的是左外連接。左邊的表是activity_items,所以不是它應該加載此表中的所有行?這就像我使用的所有查詢返回相同的結果,就像它像INNER JOIN一樣行事。

SELECT DISTINCT COUNT(DISTINCT "activity_items"."activity_itemable_id") 
FROM "activity_items" LEFT OUTER JOIN 
    "activities" ON "activities"."id" = "activity_items"."activity_id" 
WHERE (activity_date >= '2015-01-01') AND "activity_items"."activity_itemable_type" = 'Child' 

如果我使用連接而不是包括我得到了相同的結果,但與INNER JOIN。另一個奇怪的事情是:如果我沒有指定不同但只是計數(with_attribute_i_want_to_count),SQL仍然包含不同的。但是查詢的導軌指南指出,只有在給定的屬性填充到數據庫中時它才應該計數。

我測試我的查詢和結果是好的,但我不知道如何解釋和理解生成的SQL。

您如何理解這個欄中的LEFT OUTER JOIN例子?

當我用兩個表中的「includes」和條件啓動一個查詢時,我應該期待什麼?

你有一些文件來推薦澄清我的情況嗎?

感謝您閱讀這個奇怪的問題!

回答

1

您有兩個問題。

(1)「因爲在兩個不同的表上有兩個條件,使用INNER JOIN只選擇滿足兩個條件的行是不是更合適?」

內部聯接也可以充當過濾器。通過left outer join,您可以獲得沒有物品的活動。

(2)「您如何理解此導軌示例中的左外連接?」

查詢是:

SELECT DISTINCT COUNT(DISTINCT "activity_items"."activity_itemable_id") 
FROM "activity_items" LEFT OUTER JOIN 
    "activities" 
    ON "activities"."id" = "activity_items"."activity_id" 
WHERE (activity_date >= '2015-01-01') AND "activity_items"."activity_itemable_type" = 'Child' 
-------------------------------------------^ 

查詢的所提到的部分處於left outer join上第二個表的條件。這將left outer join變成inner join

故事的寓意:不要使用代碼生成器來試圖理解SQL。它們產生非常具體的代碼,可能不會像一個人會產生什麼。例如,distinct在兩個查詢中都是不必要的select distinct。這兩個查詢都不使用表別名,這會提高可讀性。所有標識都逃脫了,可能是爲了阻止人們很容易理解它們。

+0

嗨戈登感謝您的回答。這有助於我澄清我的想法。其實我使用標準的軌道「發電機」的查詢,因爲它對我來說真的很快。但是當我構建更復雜的查詢時,我決定挖掘SQL,就好像我想通過好奇心來檢查生成器的輸出。我認爲是時候對普通的SQL查詢進行深入瞭解了。你有任何閱讀推薦或教程,可以幫助我嗎?如果直接與軌道沒有關係,這不是問題。 –

+0

只是另一個小問題,當你說左外連接變爲內連接。它總是如此嗎?我的意思是如果一個左邊的外部條件在兩個表上都會是內部連接?謝謝 –

+1

如果where子句在「left join」的第二個表上有條件,並且條件過濾掉了NULL值,那麼where子句將產生與「inner join」相同的結果集。他們可能沒有相同的執行計劃,但結果是一樣的。 –