Why LINQ is better than SQL

If you're not already addicted to LINQ, you're wondering what all the fuss is about. SQL isn't broken, so why tinker with it? Why do we need another query language?

A popular saying is that LINQ is integrated with C# (or VB), thus eliminating the gap between programming languages ​​and databases, while providing a single query interface for the combination of multiple data sources. While these are facts, they are only part of the story. More importantly: LINQ is more efficient than SQL in most cases when it comes to querying the database.

Compared to SQL, LINQ is simpler, cleaner, and more advanced. This is more like comparing C# with C++. Really, while there are times when using C++ is still the best option (like in SQL scenarios), in most scenarios, using a modern clean language without having to mess with the low-level details is a big win.

SQL is a very old language - invented in 1974. While this extension has gone through countless times, it has never been redesigned. This makes it a bit confusing - not like VB6 or Visual FoxPro. You may have gotten so used to it that you can't see anything wrong!

LeoXu
LeoXu
Translated 1 year ago
0 top
Top  translation is good!
 

Let's look at an example. You want to write a simple query to get customer data like this:

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

Now suppose we want to feed this data from the result set to a web page, and we want to get rows 21 to 30 of the data. So we need a subquery:

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

And if you need to support older databases (pre-SQL Server 2005), things get even worse:

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

Doing so is not only complicated and confusing, but it also violates the DRY principle. The following is the use of LINQ to achieve the same query function. Obviously superior in simplicity:

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);

Only when we enumerate to thirdPage will the query actually execute. In a LINQ to SQL or Entity Framework scenario, the translation engine converts the query (our two-step combination) into a SQL statement optimized for the database server it is connected to.

LeoXu
LeoXu
Translated 1 year ago
0 top
Top  translation is good!
 

composability

You may have noticed another more subtle (subtle but significant) benefit of LINQ. We chose two query steps in the combination:

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

We can do this:

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

More importantly, here we can do arbitrary paginated queries. In other words, with LINQ you can break up a query into pieces and reuse them in your application.

No Ruo
No Ruo
Translated 1 year ago
1 person
Top  translation is good!
 
Other translations (1)

joint

Another benefit of LINQ is that you can query between relationships without JOINs. For example, we want to list all customers who shop $1000 or more and live in Washington. We will assume that the purchase is itemized (ie the classic purchase/item purchase scenario) and that cash sales (without customer records) are also included. This requires a query between the four tables (Purchase, Customer, Address and PurchaseItem). With LINQ, a query like this is a no-brainer:

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

Compare this to the equivalent 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     )

Extending this example further, let's say we want to reverse the result set by price and display the salesperson's name and the number of items purchased in the final projection. We can naturally express the query conditions of these attachments without repetition:

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()
}
LeoXu
LeoXu
Translated 1 year ago
0 top
Top  translation is good!
 

Here is the same query implemented using 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

What's interesting is that the above SQL query can be converted back to LINQ, and each piece of the generated query will have foolish repetitions. Forums often post such queries (often non-working versions) -  the result of thinking in SQL rather than LINQ . It's like complaining about the clunky syntax of GOTO when converting a Fortran program to C# 6.

LeoXu
LeoXu
Translated 1 year ago
0 top
Top  translation is good!
 

data trimming

Select data from multiple tables in a query union - the end result will be a flat row-wise tuple. If you've been using SQL for years, you might think that this sort of thing doesn't happen to you - it causes data duplication, which makes result sets unusable on the client side. So it's often hard to accept when it happens. In contrast, LINQ allows you to obtain restated hierarchical data. This avoids duplication, makes the result set tractable, and in most cases eliminates the need for union operations. For example, let's say we want to extract a set of customers, each record with their high value transactions. Using LINQ, you can do this:

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, here is a collection. Since we are querying a related property, there is no need for a union. Therefore, the details of whether it is an inner union or an outer union are well abstracted away. In this case, when translated to SQL, it might be an outer union: LINQ does not exclude rows because the subcollection returns zero elements. If we wanted to have something that could be translated into an inner union, we could do this:

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 also supports out-of-plane joins, self-joins, group queries, and various other types of queries through a rich set of operators.

LeoXu
LeoXu
Translated 1 year ago
1 person
Top  translation is good!
 

to parameterize

What if we wanted to parameterize the previous example, so that the "WA" state must come from a variable? Actually, we just do it like this:

string state = "WA";

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

No confusion over the parameters on the DbCommand object, or worry about SQL injection attacks. LINQ's parameterization is inline, type-safe, and highly readable. Not only did it solve the problem - but it did a good job.

Because LINQ queries can be combined, we can conditionally add predicates. For example, we write a method as follows:

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;
}
LeoXu
LeoXu
Translated 1 year ago
0 top
Top  translation is good!
 

If we call this method with an empty state and minPurchase value, the following SQL will be generated when we enumerate the result set:

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

However, if we specify values ​​for state and minPurchase, LINQ to SQL doesn't just add predicates to the query, it also has the necessary join statement:

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)

Because our method returns an IQueryable, the query is not actually converted to SQL and executed until it is enumerated. This gives the call the opportunity to further add predicates, pagination, custom projections, and more.

LeoXu
LeoXu
Translated 1 year ago
0 top
Top  translation is good!
 

static type safety

In the previous query, if we declared the state variable to be an integer instead of a string, the query could have errored out at compile time instead of at runtime. This also applies to the wrong table or column name. This has a real benefit when refactoring: if you haven't done the job at hand, the compiler will give you a hint.

client-side processing

LINQ allows you to easily offload parts of a query to be processed on the client. For heavily loaded database servers, this can actually improve performance. As long as you don't fetch more data than you need (in other words, you still have to filter on the server), you can often shift the pressure of reordering, transforming, and reorganizing the result set to a less loaded application server . With LINQ, all you need to do is move AsEnumerable() into the query, and everything from that point onwards can be performed locally.

LeoXu
LeoXu
Translated 1 year ago
1 person
Top  translation is good!
 

When to query a database without LINQ

Although LINQ is powerful, it does not replace SQL. It does more than 95% of your needs, but you still need SQL sometimes:

  • Queries that require manual tuning (especially when optimization and locking hints are required);

  • Some involve queries that select temporary tables and then perform query operations on those tables;

  • Predicted updates and bulk inserts.

Also, when using triggers, you still need SQL. (Although things like that are not often needed when using LINQ, SQL is essential when using stored procedures and functions). You can combine SQL with LINQ by writing table-valued functions in SQL, and then call these functions in more complex LINQ queries.

Knowing two query languages ​​isn't a problem, because you're going to want to learn LINQ anyway—LINQ is very useful when querying native collections as well as XML DOM. If you're still using the old XmlDocument-based DOM, LINQ to XML DOM manipulation can be a dramatic improvement.

Also, LINQ  is easier to master than SQL , so if you want to write a good query, it's better to use LINQ than SQL.

 

https://www.oschina.net/translate/why-linq-beats-sql

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325649225&siteId=291194637