自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為什么說(shuō)LINQ要?jiǎng)龠^(guò)SQL

運(yùn)維 數(shù)據(jù)庫(kù)運(yùn)維
流行的說(shuō)法是 LINQ 同 C#(或者 VB)集成在了一起,故而消除了編程語(yǔ)言和數(shù)據(jù)庫(kù)之間配合上的鴻溝,同時(shí)為多個(gè)數(shù)據(jù)源的組合提供了單一的查詢接口。雖然這些都是事實(shí),但僅是故事的一部分。更重要的是:當(dāng)要對(duì)數(shù)據(jù)庫(kù)進(jìn)行查詢的時(shí)候,LINQ 在大多數(shù)情況下都比 SQL 更加有效。

[[186565]]

如果你還沒(méi)有沉溺于 LINQ,就會(huì)想這有啥大驚小怪的。SQL 并沒(méi)有壞掉,為什么還要對(duì)它進(jìn)行修補(bǔ)呢? 為什么我們還需要另外一種查詢語(yǔ)言呢?

流行的說(shuō)法是 LINQ 同 C#(或者 VB)集成在了一起,故而消除了編程語(yǔ)言和數(shù)據(jù)庫(kù)之間配合上的鴻溝,同時(shí)為多個(gè)數(shù)據(jù)源的組合提供了單一的查詢接口。雖然這些都是事實(shí),但僅是故事的一部分。更重要的是:當(dāng)要對(duì)數(shù)據(jù)庫(kù)進(jìn)行查詢的時(shí)候,LINQ 在大多數(shù)情況下都比 SQL 更加有效。

同 SQL 相比, LINQ 更簡(jiǎn)單、整潔而且高級(jí)。這樣子更像是拿 C# 同 C++ 做比較。真的,盡管有時(shí)候使用 C++ 仍然是最好的選擇(比如使用 SQL 的場(chǎng)景),但在大多數(shù)場(chǎng)景中,使用現(xiàn)代整潔的語(yǔ)言而不必為底層細(xì)節(jié)操作就是一項(xiàng)大勝利。

SQL 是一門(mén)非常古老的語(yǔ)言—發(fā)明于 1974 年。雖然經(jīng)歷過(guò)了無(wú)數(shù)此擴(kuò)展,但從來(lái)沒(méi)有被重新設(shè)計(jì)過(guò)。這就使得它有點(diǎn)混亂了—不像是 VB6 或者 Visual FoxPro。你也許已經(jīng)慢慢變得習(xí)慣于此因而看不到任何錯(cuò)漏的地方!

讓我們來(lái)看一個(gè)例子。你想要編寫(xiě)一個(gè)簡(jiǎn)單的查詢來(lái)獲取客戶數(shù)據(jù),如下:

  1. SELECT UPPER(Name
  2.  
  3. FROM Customer 
  4.  
  5. WHERE Name LIKE 'A%' 
  6.  
  7. ORDER BY Name 

 

現(xiàn)在假設(shè)要將結(jié)果集里的這些數(shù)據(jù)提供給一個(gè)網(wǎng)頁(yè),并且我們想獲取第 21 到 30 行數(shù)據(jù)。所以我們需要一個(gè)子查詢:

  1. SELECT UPPER(NameFROM 
  2.  
  3.  
  4.    SELECT *, RN = row_number() 
  5.  
  6.    OVER (ORDER BY Name
  7.  
  8.    FROM Customer 
  9.  
  10.    WHERE Name LIKE 'A%' 
  11.  
  12. ) A 
  13.  
  14. WHERE RN BETWEEN 21 AND 30 
  15.  
  16. ORDER BY Name 

 

而如果你需要支持版本(在 SQL Server 2005 之前的)更老的數(shù)據(jù)庫(kù),情況會(huì)更糟糕:

  1. SELECT TOP 10 UPPER (c1.Name
  2.  
  3. FROM Customer c1 
  4.  
  5. WHERE 
  6.  
  7.    c1.Name LIKE 'A%' 
  8.  
  9.    AND c1.ID NOT IN 
  10.  
  11.    ( 
  12.  
  13.       SELECT TOP 20 c2.ID 
  14.  
  15.       FROM Customer c2 
  16.  
  17.       WHERE c2.Name LIKE 'A%' 
  18.  
  19.       ORDER BY c2.Name 
  20.  
  21.    )  
  22.  
  23. ORDER BY c1.Name 

 

這樣做不僅復(fù)雜而混亂,而且也違背了 DRY 原則。如下是使用 LINQ 實(shí)現(xiàn)相同的查詢功能。顯然在簡(jiǎn)單性上更勝一籌:

  1. var query = 
  2.  
  3.    from c in db.Customers 
  4.  
  5.    where c.Name.StartsWith ("A"
  6.  
  7.    orderby c.Name 
  8.  
  9.    select c.Name.ToUpper(); 
  10.  
  11.   
  12.  
  13. var thirdPage = query.Skip(20).Take(10); 

 

只有當(dāng)我們枚舉到 thirdPage 時(shí),查詢才會(huì)實(shí)際執(zhí)行。在從 LINQ 到 SQL 或者 Entity Framework 的場(chǎng)景中,翻譯引擎會(huì)將(我們用兩個(gè)步驟組合而成的)查詢轉(zhuǎn)換成一個(gè) SQL 語(yǔ)句,這個(gè)語(yǔ)句是針對(duì)其所連接的數(shù)據(jù)庫(kù)服務(wù)器進(jìn)行了優(yōu)化的。

可組合性

您可能已經(jīng)注意到 LINQ 的另一個(gè)更微妙(微妙但意義重大)的好處。我們選擇了組合中的兩個(gè)查詢步驟:

  1. IQueryable Paginate (this IQueryable query, int skip, int take) 
  2.  
  3.  
  4.    return query.Skip(skip).Take(take); 
  5.  

 

我們可以這樣做:

  1. var query = ... 
  2.  
  3. var thirdPage = query.Paginate (20, 10); 

 

更重要的是,在這里我們可以進(jìn)行任意的分頁(yè)查詢。換言之就是通過(guò) LINQ 你可以把查詢分解成一部分,然后在你的應(yīng)用程序中重用。

聯(lián)合

LINQ 另一好處就是你可以不用 JOIN 就能進(jìn)行關(guān)系間查詢。例如,我們想要列出所有購(gòu)物在 $1000 或者以上,并且居住在華盛頓的顧客。我們會(huì)假定讓購(gòu)買(mǎi)項(xiàng)目化(也就是經(jīng)典的采購(gòu)/項(xiàng)目采購(gòu)場(chǎng)景)并且把(沒(méi)有顧客記錄的)現(xiàn)金銷(xiāo)售也囊括進(jìn)來(lái)。這就需要在四個(gè)表(Purchase, Customer, Address 以及 PurchaseItem)之間進(jìn)行查詢。使用 LINQ,這樣的查詢不費(fèi)吹灰之力:

  1. from p in db.Purchases 
  2.  
  3. where p.Customer.Address.State == "WA" || p.Customer == null 
  4.  
  5. where p.PurchaseItems.Sum (pi => pi.SaleAmount) > 1000 
  6.  
  7. select p 

 

將此與同等功能的 SQL 相比較:

  1. SELECT p.* 
  2.  
  3. FROM Purchase p 
  4.  
  5.     LEFT OUTER JOIN  
  6.  
  7.         Customer c INNER JOIN Address a ON c.AddressID = a.ID 
  8.  
  9.     ON p.CustomerID = c.ID 
  10.  
  11. WHERE 
  12.  
  13.    (a.State = 'WA' || p.CustomerID IS NULL
  14.  
  15.     AND p.ID in 
  16.  
  17.     ( 
  18.  
  19.         SELECT PurchaseID FROM PurchaseItem 
  20.  
  21.         GROUP BY PurchaseID HAVING SUM (SaleAmount) > 1000 
  22.  
  23.     ) 

 

對(duì)此例進(jìn)一步擴(kuò)展,假設(shè)我們想要將結(jié)果集按價(jià)格進(jìn)行逆序排列,并在最終的投影中顯示銷(xiāo)售員的姓名以及所購(gòu)買(mǎi)項(xiàng)目的數(shù)量。我們可以自然不重復(fù)地表達(dá)出這些附件的查詢條件:

  1. from p in db.Purchases 
  2.  
  3. where p.Customer.Address.State == "WA" || p.Customer == null 
  4.  
  5. let purchaseValue = p.PurchaseItems.Sum (pi => pi.SaleAmount) 
  6.  
  7. where purchaseValue > 1000 
  8.  
  9. orderby purchaseValue descending 
  10.  
  11. select new 
  12.  
  13.  
  14.    p.Description, 
  15.  
  16.    p.Customer.SalesPerson.Name
  17.  
  18.    PurchaseItemCount = p.PurchaseItems.Count() 
  19.  

 

下面是使用 SQL 實(shí)現(xiàn)相同的查詢:

  1. SELECT  
  2.  
  3.     p.Description, 
  4.  
  5.     s.Name
  6.  
  7.     (SELECT COUNT(*) FROM PurchaseItem pi WHERE p.ID = pi.PurchaseID) PurchaseItemCount 
  8.  
  9. FROM Purchase p 
  10.  
  11.     LEFT OUTER JOIN  
  12.  
  13.         Customer c  
  14.  
  15.             INNER JOIN Address a ON c.AddressID = a.ID 
  16.  
  17.             LEFT OUTER JOIN SalesPerson s ON c.SalesPersonID = s.ID 
  18.  
  19.     ON p.CustomerID = c.ID 
  20.  
  21. WHERE 
  22.  
  23.     (a.State = 'WA' OR p.CustomerID IS NULL
  24.  
  25.     AND p.ID in 
  26.  
  27.     ( 
  28.  
  29.         SELECT PurchaseID FROM PurchaseItem 
  30.  
  31.         GROUP BY PurchaseID HAVING SUM (SaleAmount) > 1000 
  32.  
  33.     ) 
  34.  
  35. ORDER BY 
  36.  
  37.     (SELECT SUM (SaleAmount) FROM PurchaseItem pi WHERE p.ID = pi.PurchaseID) DESC 

 

有意思的是可以將上述 SQL 查詢轉(zhuǎn)換回到 LINQ,所生成的查詢每一塊都會(huì)有傻瓜式重復(fù)。論壇里常會(huì)貼出這樣的查詢(通常是非工作的版本)——這是用 SQL 進(jìn)行思考而不是以 LINQ 進(jìn)行思考的結(jié)果。這就像是是將 Fortran 程序轉(zhuǎn)換成 C# 6 時(shí)會(huì)抱怨 GOTO 的笨拙語(yǔ)法一樣。

數(shù)據(jù)修整

在查詢聯(lián)合中從多個(gè)表選擇數(shù)據(jù) – 最終的結(jié)果會(huì)是一個(gè)扁平的以行為單位的元組。如果你使用了多年的 SQL,你可能認(rèn)為這種事不會(huì)發(fā)生在你身上——它導(dǎo)致數(shù)據(jù)重復(fù),從而使得結(jié)果集無(wú)法在客戶端很好地使用。所以當(dāng)它發(fā)生時(shí)往往難以接受。與此相反,LINQ 讓你可以獲取到休整過(guò)的分層級(jí)的數(shù)據(jù)。這就避免了重復(fù),讓結(jié)果集容易處理,而且在大多數(shù)情況下也會(huì)消除進(jìn)行聯(lián)合操作的必要。例如,假設(shè)我們想要提取一組顧客,每一條記錄都帶上了它們的高價(jià)值交易。使用 LINQ,你可以這樣做:

  1. from c in db.Customers 
  2.  
  3. where c.Address.State == "WA" 
  4.  
  5. select new 
  6.  
  7.  
  8.    c.Name
  9.  
  10.    c.CustomerNumber, 
  11.  
  12.    HighValuePurchases = c.Purchases.Where (p => p.Price > 1000) 
  13.  

 

HighValuePurchases,在這里是一個(gè)集合。由于我們查詢的是一個(gè)相關(guān)屬性,就不需要進(jìn)行聯(lián)合了。因此這是一個(gè)內(nèi)聯(lián)合還是外聯(lián)合的細(xì)節(jié)問(wèn)題就被很好的抽象掉了。在此例中,當(dāng)翻譯成了 SQL,可能就是一個(gè)外聯(lián)合:LINQ 不會(huì)因?yàn)樽蛹戏祷氐氖橇銈€(gè)元素就排除行。如果我們想要有一個(gè)可以翻譯成一個(gè)內(nèi)聯(lián)合的東西,可以這樣做:

 

  1. from c in db.Customers 
  2.  
  3. where c.Address.State == "WA" 
  4.  
  5. let HighValuePurchases = c.Purchases.Where (p => p.Price > 1000)where HighValuePurchases.Any()select new 
  6.  
  7.  
  8.    c.Name
  9.  
  10.    c.CustomerNumber, 
  11.  
  12.    HighValuePurchases 
  13.  

 

LINQ 還通過(guò)一組豐富的操作符對(duì)平面外聯(lián)合、自聯(lián)合、組查詢以及其它各種不同類(lèi)型查詢進(jìn)行了支持。

參數(shù)化

如果我們想要將之前的例子參數(shù)化會(huì)如何呢,如此”WA”狀態(tài)是不是就要來(lái)自于一個(gè)變量呢? 其實(shí)我們只要像下面這樣做就可以了:

  1. string state = "WA"
  2.  
  3.   
  4.  
  5. var query = 
  6.  
  7.    from c in db.Customers 
  8.  
  9.    where c.Address.State == state 
  10.  
  11.    ... 

 

不會(huì)混淆 DbCommand 對(duì)象上面的參數(shù),或者擔(dān)心 SQL 注入攻擊。 LINQ 的參數(shù)化是內(nèi)聯(lián)、類(lèi)型安全并且高度可讀的。它不僅解決了問(wèn)題——而且解決得很不錯(cuò)。

因?yàn)?LINQ 查詢時(shí)可以進(jìn)行組合,所以我們可以有條件的添加謂詞。例如,我們寫(xiě)出一個(gè)方法,如下:

  1. IQueryable GetCustomers (string state, decimal? minPurchase) 
  2.  
  3.  
  4.     var query = Customers.AsQueryable(); 
  5.  
  6.      
  7.  
  8.     if (state != null
  9.  
  10.         query = query.Where (c => c.Address.State == state); 
  11.  
  12.      
  13.  
  14.     if (minPurchase != null
  15.  
  16.         query = query.Where (c => c.Purchases.Any (p => p.Price > minPurchase.Value)); 
  17.  
  18.      
  19.  
  20.     return query; 
  21.  

 

如果我們使用空的 state 以及 minPurchase 值調(diào)用了這個(gè)方法,那么在我們枚舉結(jié)果集的時(shí)候如下 SQL 就會(huì)被生成出來(lái):

  1. SELECT [t0].[ID], [t0].[Name], [t0].[AddressID] 
  2.  
  3. FROM [Customer] AS [t0] 

 

不過(guò),如果我們指定了 state 和 minPurchase 的值,LINQ 到 SQL 就不只是向查詢添加了謂詞,還會(huì)有必要的聯(lián)合語(yǔ)句:

  1. SELECT [t0].[ID], [t0].[Name], [t0].[AddressID] 
  2.  
  3. FROM [Customer] AS [t0] 
  4.  
  5. LEFT OUTER JOIN [Address] AS [t1] ON [t1].[ID] = [t0].[AddressID] 
  6.  
  7. WHERE (EXISTS( 
  8.  
  9.     SELECT NULL AS [EMPTY] 
  10.  
  11.     FROM [Purchase] AS [t2] 
  12.  
  13.     WHERE ([t2].[Price] > @p0) AND ([t2].[CustomerID] = [t0].[ID]) 
  14.  
  15.     )) AND ([t1].[State] = @p1) 

 

因?yàn)槲覀兊姆椒ǚ祷亓艘粋€(gè) IQueryable,查詢?cè)诿杜e到之前并不會(huì)被實(shí)際地轉(zhuǎn)換成 SQL 并加以執(zhí)行。這樣就給了調(diào)用進(jìn)一步添加謂詞、分頁(yè)、自定義投影等等的機(jī)會(huì)。

靜態(tài)類(lèi)型安全

在之前的查詢中,如果我們將 state 變量聲明成了一個(gè)整型數(shù)而不是一個(gè)字符串,那么查詢可能在編譯時(shí)就會(huì)報(bào)錯(cuò),而不用等到運(yùn)行時(shí)。這個(gè)也同樣適用于把表名或者列名弄錯(cuò)的情況。這在重構(gòu)時(shí)有一個(gè)很實(shí)在的好處:如果你沒(méi)有完成手頭的工作,編譯器會(huì)給出提示。

客戶端處理

LINQ 讓你可以輕松地將查詢的一些部分轉(zhuǎn)移到客戶端上進(jìn)行處理。對(duì)于負(fù)載負(fù)擔(dān)較大的數(shù)據(jù)庫(kù)服務(wù)器,這樣做可實(shí)際提升性能。只要你所取數(shù)據(jù)沒(méi)有超過(guò)所需(換言之,你還是要在服務(wù)器上做過(guò)濾),就可以經(jīng)常性地通過(guò)把對(duì)結(jié)果集進(jìn)行重新排序、轉(zhuǎn)換以及重組的壓力轉(zhuǎn)移到負(fù)載較少的應(yīng)用服務(wù)器上去。使用 LINQ,你需要做的就是 AsEnumerable() 轉(zhuǎn)移到查詢之中,而自那個(gè)點(diǎn)之后的所有事情都可以在本地執(zhí)行。

什么時(shí)候不用 LINQ 去查詢數(shù)據(jù)庫(kù)

盡管 LINQ 的功能強(qiáng)大,但是它并不能取代 SQL。它可以滿足 95% 以上的需求,不過(guò)你有時(shí)仍然需要SQL:

  • 需要手動(dòng)調(diào)整的查詢 (特殊是需要優(yōu)化和進(jìn)行鎖定提示的時(shí)候);
  • 有些涉及到要 select 臨時(shí)表,然后又要對(duì)那些表進(jìn)行查詢操作的查詢;
  • 預(yù)知的更新以及批量插入操作。

還有就在用到觸發(fā)器時(shí),你還是需要 SQL。 (盡管在使用 LINQ 的時(shí)候諸如此類(lèi)的東西并非常常被需要,但在要使用存儲(chǔ)過(guò)程和函數(shù)的時(shí)候,SQL 是不可或缺的)。你可以通過(guò)在 SQL 中編寫(xiě)表值函數(shù)來(lái)將 SQL 與 LINQ 結(jié)合在一起, 然后在更加復(fù)雜的 LINQ 查詢里面調(diào)用這些函數(shù)。

了解兩門(mén)查詢語(yǔ)言并不是問(wèn)題,因?yàn)闊o(wú)論如何你都會(huì)想要去學(xué)習(xí) LINQ 的 — LINQ 在查詢本地集合以及 XML DOM 的時(shí)候非常實(shí)用。如果你使用的仍然是老舊的基于 XmlDocument 的 DOM,LINQ to XML 的 DOM 操作會(huì)是一種具有戲劇效果的進(jìn)步。

還有就是相比于 SQL, LINQ 更易于掌握,所以如果你想寫(xiě)個(gè)不錯(cuò)的查詢,使用 LINQ 會(huì)比 SQL 更好達(dá)成。

將 LINQ 用于實(shí)戰(zhàn)

我?guī)缀跏侵挥?LINQ 來(lái)做數(shù)據(jù)庫(kù)查詢,因?yàn)樗行省?/p>

對(duì)于應(yīng)用程序的編寫(xiě)而言,我的個(gè)人經(jīng)驗(yàn)是一個(gè)使用 LINQ 的數(shù)據(jù)訪問(wèn)層(使用一個(gè)像 LINQ 到 SQL 或者 Entity Framework 的 API)可以將數(shù)據(jù)訪問(wèn)的開(kāi)發(fā)時(shí)間砍掉一半,而且可以讓維護(hù)工作更加的輕松。 

責(zé)任編輯:龐桂玉 來(lái)源: 數(shù)據(jù)庫(kù)開(kāi)發(fā)
相關(guān)推薦

2015-11-26 09:10:30

2016-07-01 14:37:01

SparkSQL

2021-04-12 10:28:51

機(jī)器學(xué)習(xí)人工智能AI

2022-11-15 08:35:00

SQLNOLOCK數(shù)據(jù)

2023-04-06 08:43:29

SQLWITH(NOLOCK

2022-06-09 08:32:21

SQLNOLOCKWITH

2021-09-14 10:48:13

SQL Nolock代碼

2018-06-21 09:30:50

比特幣區(qū)塊鏈擴(kuò)容

2016-09-22 16:06:21

微服務(wù)架構(gòu)RPC框架

2011-08-01 14:33:44

SQL

2011-11-08 09:18:42

云計(jì)算開(kāi)源OpenStack

2020-12-20 17:37:38

Java開(kāi)發(fā)代碼

2015-08-06 10:14:15

造輪子facebook

2022-08-15 08:27:02

基站網(wǎng)絡(luò)

2013-03-12 14:30:09

Ubuntu操作系統(tǒng)

2018-08-21 21:55:53

2016-12-14 12:02:01

StormHadoop大數(shù)據(jù)

2017-02-14 14:20:02

StormHadoop

2017-09-08 08:35:16

Android代碼API設(shè)計(jì)

2024-10-17 16:41:57

KafkaZooKeeper
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)