主页»ASP.NET»为什么说 LINQ 要胜过 SQL

为什么说 LINQ 要胜过 SQL

来历:oschina 发布时刻:2017-03-01 阅览次数:

  假如你还没有沉溺于 LINQ,就会想这有啥少见多怪的。SQL 并没有坏掉,为什么还要对它进行修补呢? 为什么咱们还需求别的一种查询言语呢?

  盛行的说法是 LINQ 同 C#(或许 VB)集成在了一同,故而消除了编程言语和数据库之间配合上的距离,一起为多个数据源的组合供给了单一的查询接口。虽然这些都是现实,但仅是故事的一部分。更重要的是:当要对数据库进行查询的时分,LINQ 在大多数状况下都比 SQL 愈加有用。

  同 SQL 比较, LINQ 更简略、整齐并且高档。这姿态更像是拿 C# 同 C++ 做比较。真的,虽然有时分运用 C++ 依然是最好的挑选(比方运用 SQL 的场景),但在大多数场景中,运用现代整齐的言语而不必为底层细节操作便是一项大成功。

  SQL 是一门十分陈旧的言语—发明于 1974 年。虽然阅历过了很多此扩展,但从来没有被从头规划过。这就使得它有点紊乱了—不像是 VB6 或许 Visual FoxPro。你或许现已渐渐变得习气于此因而看不到任何讹夺的当地!

  让咱们来看一个比如。你想要编写一个简略的查询来获取客户数据,如下:

SELECT UPPER(Name)
FROM Customer
WHERE Name LIKE 'A%'
ORDER BY Name

  现在假定要将成果集里的这些数据供给给一个网页,并且咱们想获取第 21 到 30 行数据。所以咱们需求一个子查询:

SELECT UPPER(Name) FROM
(
   SELECT *, RN = row_number()
   OVER (ORDER BY Name)
   FROM Customer
   WHERE Name LIKE 'A%'
) A
WHERE RN BETWEEN 21 AND 30
ORDER BY Name

  而假如你需求支撑版别(在 SQL Server 2005 之前的)更老的数据库,状况会更糟糕:

SELECT TOP 10 UPPER (c1.Name)
FROM Customer c1
WHERE
   c1.Name LIKE 'A%'
   AND c1.ID NOT IN
   (
      SELECT TOP 20 c2.ID
      FROM Customer c2
      WHERE c2.Name LIKE 'A%'
      ORDER BY c2.Name
   ) 
ORDER BY c1.Name

  这样做不只杂乱而紊乱,并且也违反了 DRY 准则。如下是运用 LINQ 完结相同的查询功用。显然在简略性上更胜一筹:

var query =
   from c in db.Customers
   where c.Name.StartsWith ("A")
   orderby c.Name
   select c.Name.ToUpper();

var thirdPage = query.Skip(20).Take(10);

  只需当咱们枚举到 thirdPage 时,查询才会实践履行。在从 LINQ 到 SQL 或许 Entity Framework 的场景中,翻译引擎会将(咱们用两个进程组合而成的)查询转化成一个 SQL 句子,这个句子是针对其所衔接的数据库服务器进行了优化的。

  可组合性

  您或许现已注意到 LINQ 的另一个更奇妙(奇妙但含义严重)的优点。咱们挑选了组合中的两个查询进程:

IQueryable<T> Paginate<T> (this IQueryable<T> query, int skip, int take)
{
   return query.Skip(skip).Take(take);
}

  咱们能够这样做:

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

  更重要的是,在这里咱们能够进行恣意的分页查询。换言之便是经过 LINQ 你能够把查询分解成一部分,然后在你的应用程序中重用。

  联合

  LINQ 另一优点便是你能够不必 JOIN 就能进行联系间查询。例如,咱们想要列出一切购物在 $1000 或许以上,并且居住在华盛顿的顾客。咱们会假定让购买项目化(也便是经典的收购/项目收购场景)并且把(没有顾客记载的)现金出售也包括进来。这就需求在四个表(Purchase, Customer, Address 以及 PurchaseItem)之间进行查询。运用 LINQ,这样的查询不费吹灰之力:

from p in db.Purchases
where p.Customer.Address.State == "WA" || p.Customer == null
where p.PurchaseItems.Sum (pi => pi.SaleAmount) > 1000
select p

  将此与平等功用的 SQL 比较较:

SELECT p.*
FROM Purchase p
    LEFT OUTER JOIN 
        Customer c INNER JOIN Address a ON c.AddressID = a.ID
    ON p.CustomerID = c.ID	
WHERE
   (a.State = 'WA' || p.CustomerID IS NULL)
    AND p.ID in
    (
        SELECT PurchaseID FROM PurchaseItem
        GROUP BY PurchaseID HAVING SUM (SaleAmount) > 1000
    )

  对此例进一步扩展,假定咱们想要将成果集按价格进行逆序摆放,并在终究的投影中显现出售员的名字以及所购买项目的数量。咱们能够天然不重复地表达出这些附件的查询条件:

from p in db.Purchases
where p.Customer.Address.State == "WA" || p.Customer == null
let purchaseValue = p.PurchaseItems.Sum (pi => pi.SaleAmount)
where purchaseValue > 1000
orderby purchaseValue descending
select new
{
   p.Description,
   p.Customer.SalesPerson.Name,
   PurchaseItemCount = p.PurchaseItems.Count()
}

  下面是运用 SQL 完结相同的查询:

SELECT 
    p.Description,
    s.Name,
    (SELECT COUNT(*) FROM PurchaseItem pi WHERE p.ID = pi.PurchaseID) PurchaseItemCount	
FROM Purchase p
    LEFT OUTER JOIN 
        Customer c 
            INNER JOIN Address a ON c.AddressID = a.ID
            LEFT OUTER JOIN SalesPerson s ON c.SalesPersonID = s.ID
    ON p.CustomerID = c.ID	
WHERE
    (a.State = 'WA' OR p.CustomerID IS NULL)
    AND p.ID in
    (
        SELECT PurchaseID FROM PurchaseItem
        GROUP BY PurchaseID HAVING SUM (SaleAmount) > 1000
    )
ORDER BY
    (SELECT SUM (SaleAmount) FROM PurchaseItem pi WHERE p.ID = pi.PurchaseID) DESC

  有意思的是能够将上述 SQL 查询转化回到 LINQ,所生成的查询每一块都会有傻瓜式重复。论坛里常会贴出这样的查询(一般对错作业的版别)——这是用 SQL 进行考虑而不是以 LINQ 进行考虑的成果。这就像是是将 Fortran 程序转化成 C# 6 时会诉苦 GOTO 的蠢笨语法相同。

  数据修整

  在查询联合中从多个表挑选数据 - 终究的成果会是一个扁平的以行为单位的元组。假如你运用了多年的 SQL,你或许以为这种事不会发作在你身上——它导致数据重复,然后使得成果集无法在客户端很好地运用。所以当它发作时往往难以承受。与此相反,LINQ 让你能够获取到休整过的分层级的数据。这就避免了重复,让成果集简单处理,并且在大多数状况下也会消除进行联合操作的必要。例如,假定咱们想要提取一组顾客,每一条记载都带上了它们的高价值买卖。运用 LINQ,你能够这样做:

from c in db.Customers
where c.Address.State == "WA"
select new
{
   c.Name,
   c.CustomerNumber,
   HighValuePurchases = c.Purchases.Where (p => p.Price > 1000)
}

  HighValuePurchases,在这里是一个调集。由于咱们查询的是一个相关特点,就不需求进行联合了。因而这是一个内联合仍是外联合的细节问题就被很好的笼统掉了。在此例中,当翻译成了 SQL,或许便是一个外联合:LINQ 不会由于子调集回来的是零个元素就扫除行。假如咱们想要有一个能够翻译成一个内联合的东西,能够这样做:

from c in db.Customers
where c.Address.State == "WA"
let HighValuePurchases = c.Purchases.Where (p => p.Price > 1000)where HighValuePurchases.Any()select new
{
   c.Name,
   c.CustomerNumber,
   HighValuePurchases
}

  LINQ 还经过一组丰厚的操作符对平面外联合、自联合、组查询以及其它各种不同类型查询进行了支撑。

  参数化

  假如咱们想要将之前的比如参数化会怎么呢,如此"WA"状况是不是就要来自于一个变量呢? 其实咱们只需像下面这样做就能够了:

string state = "WA";

var query =
   from c in db.Customers
   where c.Address.State == state
   ...

  不会混杂 DbCommand 目标上面的参数,或许忧虑 SQL 注入进犯。 LINQ 的参数化是内联、类型安全并且高度可读的。它不只处理了问题——并且处理得很不错。

  由于 LINQ 查询时能够进行组合,所以咱们能够有条件的增加谓词。例如,咱们写出一个办法,如下:

IQueryable<Customer> GetCustomers (string state, decimal? minPurchase)
{
    var query = Customers.AsQueryable();
    
    if (state != null)
        query = query.Where (c => c.Address.State == state);
    
    if (minPurchase != null)
        query = query.Where (c => c.Purchases.Any (p => p.Price > minPurchase.Value));
    
    return query;
}

  假如咱们运用空的 state 以及 minPurchase 值调用了这个办法,那么在咱们枚举成果集的时分如下 SQL 就会被生成出来:

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

  不过,假如咱们指定了 state 和 minPurchase 的值,LINQ 到 SQL 就不仅仅向查询增加了谓词,还会有必要的联合句子:

SELECT [t0].[ID], [t0].[Name], [t0].[AddressID]
FROM [Customer] AS [t0]
LEFT OUTER JOIN [Address] AS [t1] ON [t1].[ID] = [t0].[AddressID]
WHERE (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM [Purchase] AS [t2]
    WHERE ([t2].[Price] > @p0) AND ([t2].[CustomerID] = [t0].[ID])
    )) AND ([t1].[State] = @p1)

  由于咱们的办法回来了一个 IQueryable,查询在枚举到之前并不会被实践地转化成 SQL 并加以履行。这样就给了调用进一步增加谓词、分页、自定义投影等等的时机。

  静态类型安全

  在之前的查询中,假如咱们将 state 变量声明成了一个整型数而不是一个字符串,那么查询或许在编译时就会报错,而不必比及运行时。这个也相同适用于把表名或许列名弄错的状况。这在重构时有一个很真实的优点:假如你没有完结手头的作业,编译器会给出提示。

  客户端处理

  LINQ 让你能够轻松地将查询的一些部分转移到客户端上进行处理。关于负载担负较大的数据库服务器,这样做可实践提高功用。只需你所取数据没有超越所需(换言之,你仍是要在服务器上做过滤),就能够常常性地经过把对成果集进行从头排序、转化以及重组的压力转移到负载较少的应用服务器上去。运用 LINQ,你需求做的便是 AsEnumerable() 转移到查询之中,而自那个点之后的一切作业都能够在本地履行。

  什么时分不必 LINQ 去查询数据库

  虽然 LINQ 的功用强壮,可是它并不能替代 SQL。它能够满意 95% 以上的需求,不过你有时依然需求SQL:

  • 需求手动调整的查询 (特别是需求优化和进行确定提示的时分);

  • 有些涉及到要 select 暂时表,然后又要对那些表进行查询操作的查询;

  • 预知的更新以及批量刺进操作。

  还有就在用到触发器时,你仍是需求 SQL。 (虽然在运用 LINQ 的时分诸如此类的东西并十分常被需求,但在要运用存储进程和函数的时分,SQL 是不可或缺的)。你能够经过在 SQL 中编写表值函数来将 SQL 与 LINQ 结合在一同, 然后在愈加杂乱的 LINQ 查询里边调用这些函数。

  了解两门查询言语并不是问题,由于无论怎么你都会想要去学习 LINQ 的 — LINQ 在查询本地调集以及 XML DOM 的时分十分有用。假如你运用的依然是老旧的根据 XmlDocument 的 DOM,LINQ to XML 的 DOM 操作会是一种具有戏曲作用的前进。

  还有便是比较于 SQL, LINQ 更易于把握,所以假如你想写个不错的查询,运用 LINQ 会比 SQL 更好达到。

  将 LINQ 用于实战

  我几乎是只用 LINQ 来做数据库查询,由于它更有用率。

  关于应用程序的编写而言,我的个人经历是一个运用 LINQ 的数据拜访层(运用一个像 LINQ 到 SQL 或许 Entity Framework 的 API)能够将数据拜访的开发时刻砍掉一半,并且能够让保护作业愈加的轻松。

  原文地址:https://www.linqpad.net/WhyLINQBeatsSQL.aspx

QQ群:凯发娱乐官网官方群(515171538),验证音讯:10000
微信群:加小编微信 849023636 邀请您参加,验证音讯:10000
提示:更多精彩内容重视微信大众号:全栈开发者中心(fsder-com)
m88 188bet uedbet 威廉希尔 明升 bwin 明升88 bodog bwin 明升m88.com 18luck 188bet unibet unibet Ladbrokes Ladbrokes casino m88明升 明升 明升 m88.com 188bet m88 明陞 uedbet赫塔菲官网 365bet官网 m88 help
网友谈论(共0条谈论) 正在载入谈论......
沉着谈论文明上网,回绝歹意咒骂 宣布谈论 / 共0条谈论
登录会员中心