我打算給出不同的例子,因爲我不太關注你的,並用它來解釋加入的基本知識,希望能達到你需要學習的東西。我們設想兩個比LocationThing等稍微更有意義的類(它使我失去了)。
public class Language
{
string Code{get;set;}
string EnglishName{get;set;}
string NativeName{get;set;}
}
public class Document
{
public int ID{get; private set;}//no public set as it corresponds to an automatically-set column
public string LanguageCode{get;set;}
public string Title{get;set;}
public string Text{get;set;}
}
現在,讓我們也想像我們有分別返回所有的語言和文檔的方法GetLanguages()
和GetDocuments()
。有幾種不同的方式可以工作,我會在稍後討論。
一個有用的連接的例子是,如果我們例如希望所有的標題和他們在語言的所有的英文名,在SQL我們將使用:
SELECT documents.title, languages.englishName
FROM languages JOIN documents
ON languages.code = documents.languageCode
或離開在哪裏這樣做不會使列名不明確的表名:
SELECT title, englishName
FROM languages JOIN documents
ON code = languageCode
對於文檔中的每一行,它們中的每一行都會將它們與語言中的對應行進行匹配,並返回組合行的標題和英文名稱(如果沒有匹配語言的文檔, t返回,如果有兩種語言具有相同的代碼 - 在這種情況下應該被db阻止 - 相應的文檔會被提到一次ea CH)。
的LINQ當量是:
from l in GetLanguages()
join d in GetDocuments()
on l.Code equals d.LanguageCode //note l must come before d
select new{d.Title, l.EnglishName}
這將同樣每個文檔與其相應的語言相匹配,並返回一個IQueryable<T>
或IEnumerable<T>
(取決於來源枚舉/ queryables上),其中T
與Title
和一個匿名對象EnglishName
屬性。
現在,至於這方面的費用。這主要取決於GetLanguages()
和GetDocuments()
的性質。
不管來源如何,這本質上都是搜索這兩種方法的每一個結果的問題 - 這只是操作的本質。然而,這樣做的最有效的方式仍然是根據我們對源數據的瞭解而變化的。我們首先考慮一下Linq2Objects表單。有很多是可以這樣做的方式,但讓我們來想象他們返回List
s表示是預先計算:
public List<Document> GetDocuments()
{
return _precomputedDocs;
}
public List<Language> GetLanguages()
{
return _precomputedLangs;
}
讓我們假設的LINQ的join
不存在了一會兒,並想象我們如何想寫一些功能等同於上面的代碼。我們可能會得到類似於:
var langLookup = GetLanguages().ToLookup(l => l.Code);
foreach(var doc in GetDocuments())
foreach(var lang in langLookup[doc.LanguageCode])
yield return new{doc.Title, lang.EnglishName};
這是一個合理的一般情況。我們可以走得更遠一步,並減少存儲,因爲我們知道,所有我們最終關心的各語言是英文名稱:
var langLookup = GetLanguages().ToLookup(l => l.Code, l => l.EnglishName);
foreach(var doc in GetDocuments())
foreach(var englishName in langLookup[doc.LanguageCode])
yield return new{doc.Title, EnglishName = englishName};
這是對不亞於我們可以做而不集的專業知識數據。
如果我們確實有特殊的知識,我們可以走得更遠。舉例來說,如果我們知道有每個代碼只使用一種語言,那麼下面會更快:
var langLookup = GetLanguages().ToDictionary(l => l.Code, l => l.EnglishName);
string englishName;
foreach(var doc in GetDocuments())
if(langLookup.TryGetValue(doc.LanguageCode, out englishName))
yield return new{doc.Title, EnglishName = englishName};
如果我們知道這兩個源都是由語言代碼排序,我們仍然可以走得更遠,並通過它們旋轉兩者同時產生匹配,並且在我們處理完它們之後扔掉語言,因爲我們在枚舉的其餘部分再也不需要它了。
但是,Linq在查看兩個列表時並沒有那些特殊的知識。衆所周知,每一種語言和每一份文件都有相同的代碼。它真的要檢查很多東西才能發現。爲此,它的效率非常高(比上面的例子好一些,由於一些優化)。
讓我們考慮一個Linq2SQL的情況,並且注意實體框架和直接在數據庫上使用Linq的其他方式是可比的。假設所有這些都發生在具有_ctx
成員(DataContext
)的類的上下文中。那麼,我們的源方法可能是:
public Table<Document> GetDocuments()
{
return _ctx.GetTable<Document>();
}
public Table<Language> GetLanguages()
{
return _ctx.GetTable<Languages>();
}
Table<T>
一些其他方法一起實現IQueryable<T>
。在這裏,不是加入內存中的東西,它會執行以下操作(禁止一些別名)SQL:
SELECT documents.title, languages.englishName
FROM languages JOIN documents
ON languages.code = documents.languageCode
看起來很熟悉嗎?這是我們在開始時提到的相同的SQL。
這件事的第一件好事就是它不會從數據庫中取回任何我們不會使用的東西。
第二件好事,就是數據庫的查詢引擎(將它變成可執行代碼,然後運行)確實瞭解數據的性質。例如,如果我們將Languages
表設置爲在code
列上具有唯一鍵或約束,則引擎知道不會有兩種語言具有相同的代碼,因此它可以執行與我們上面提到的優化我們使用Dictionary
而不是ILookup
。
三偉大的事情是,如果我們有languages.code
和documents.languageCode
指數則查詢引擎將使用這些獲得更快的檢索和匹配,也許讓所有從索引需要沒有擊中表,撥打電話,以哪個表首先命中以避免在第二個測試中不相關的行,等等。
第四件偉大的事情是,RDBMS已經從數十年的研究中受益,如何儘快進行這種檢索,所以我們有一些事情我不知道和不需要了解如何從中受益。
總之,我們希望直接對數據源運行查詢,而不是針對內存中的源。有一些例外情況,特別是某些形式的分組(如果直接點擊數據庫,則某些分組操作可能意味着多次觸發它),如果我們快速連續重複使用相同的結果(在這種情況下,我們會更好爲這些結果命中一次,然後存儲它們)。
我不確定要了解「JOIN」需要做什麼?首先你應該定義你需要什麼類型的回報?它是一個TheCellThing []? – 2012-08-03 14:14:03
@MarinoŠimić我需要調用TheCellThing上的每個匹配的方法。我想連接會優化查詢通過攔截比賽便宜,然後做一個雙擊foreach循環。如果還有其他方法,我很樂意聽到。 – 2012-08-03 14:32:55