Tại sao LINQ lại “đánh bại” SQL?

324 lượt xem

Hầu hết các lập trình viên phát triển Web theo hướng ASP.NET đều tìm hiểu và sử dụng LINQ, như vậy lý do đó là gì? Bài viết này sẽ giải đáp các lý do tại sao LINQ có nhiều ưu điểm vượt trội hơn so với SQL.

Một trong các lý do hàng đầu để LINQ nhận sự quan tâm lớn chính là nó tích hợp với C# (hay VB), vì vậy giảm thiểu khoảng trống thiếu tương thích giữa các ngôn ngữ lập trình và cơ sở dữ liệu, cũng như cung cấp 1 giao diện truy vấn đơn giản cho tất cả các nguồn dữ liệu. Quan trọng hơn là, khi truy vấn database, LINQ có khả năng thực thi năng suất (xét về mặt bằng chung) tốt hơn so với SQL.

Thật vậy, nếu so sánh với SQL, LINQ đơn giản hơn, gọn hơn và mã nguồn ở cấp độ cao hơn. LINQ đôi khi xem xét để so sánh với C#, C++. SQL là ngôn ngữ truy vấn lâu đời, được phát minh vào năm 1974. Từ đó, SQL được mở rộng dường như vô tận, nhưng lại không được tái cấu trúc lại. Điều đó là cho SQL đôi khi hơi lộn xộn, tương tự như VB6 và Visual FoxPro. Nếu bạn không đồng ý nghĩa điều trên có thể là do bạn đã quá quen thuộc với SQL.

Hãy làm ví dụ để thấy rõ sự so sánh giữa LINQ và SQL. Chúng ta có 1 đoạn SQL đơn giản như sau:

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

Ví dụ trên bạn cho tất cả tên (Name) của khách hàng bắt đầu từ vần A và xuất kết quả in hoa danh sách tên khách hàng. Nếu bạn cần lấy các record từ database từ dòng 21 đến dòng 30. Bạn cần viết thêm 1 query phụ như sau:

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

Nếu bạn dùng database SQL 2005 trở về trước thì câu truy vấn dài hơn 1 chút:

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

Mặc dù các câu truy vấn trên không phức tạp, nhưng nó lại vi phạm nguyên lý DRY (DON’T REPEAT YOURSELF tức là KHÔNG LẶP LẠI CHÍNH BẠN). Tiếp đến, chúng ta xét câu lệnh LINQ ngắn gọn, dễ hiểu như sau:

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

Chỉ khi nào chúng ta truy xuất biến thirdPage câu truy vấn mới thực thi. Trong trường hợp, LINQ sang SQL hay Entity Framework, cơ chế diễn dịch sẽ chuyển truy vấn (cái mà chúng ta chia thành 2 bước trong ví dụ trên) thành 1 câu SQL tối ưu để truy vấn cơ sở dữ liệu.

Tính khả thi
Bạn có thể quan tâm đến lợi ích tinh tế (nhưng quan trọng) theo hướng tiếp cận LINQ. Chúng ta tạo câu truy vấn trong 2 bước cho phép xây dựng bước thứ 2 vào 1 phương thức có thể tái sử dụng như sau:

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

Sau đó, bạn thể viết truy vấn trên như sau:

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

Vấn đề quan trọng là chúng ta có thể áp dụng phương thức Paginate với bất kỳ truy vấn nào. Nói cách khác, với LINQ bạn có thể chia câu truy vấn thành nhiều phần, và tái sử dụng các phần truy vấn cho toàn bộ ứng dụng của bạn.

Tính liên kết
Một lợi ích khác của LINQ là bạn có thể truy vấn theo các mối quan hệ mà không cần phải join. Ví dụ, nếu bạn muốn liệt kê danh sách các hóa đơn có giá trị bằng hoặc lớn hơn $1000 của các khách hàng ở Washington. Chúng ta giả định các hóa đơn được liên kết (theo mô hình giữa 1-n giữa hai bảng Purchase và PurchaseItem) và chúng ta muốn bao gồm các hóa đơn tiền mặt (không có tên khách hàng). Để truy vấn, chúng ta dùng 4 bảng Purchase, Customer, Address và PurchaseItem.

Trong LINQ, câu truy vấn được viết như sau:

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

Trong SQL, câu truy vấn được viết như sau:

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
    )

Mở rộng thêm ví dụ này, chúng ta muốn các kết quả hiển thị theo thứ tự đảo ngược về giá cả, và tên người bán và số lượng item sản phẩm trong kết quả truy vấn. Chú ý cách tính tự nhiên chúng ta mô tả các tham số mà không cần lặp lại trong LINQ.

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()
}

Đây là đoạn code trong SQL có cùng chức năng:

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

Điểm thú vị là có thể chuyển dịch câu truy vấn trên từ SQL ngược lại thành LINQ, tuy nhiên điều đó mang lại 1 câu truy vấn bị lặp lại đôi chút và hơi rối. Do đó bạn có thể thấy LINQ “tốt hơn” so với SQL ở việc liên kết dữ liệu giữa nhiều thực thể (bảng).

Ngoài ra, còn có nhiều tiêu chí khác để so sánh như về thông số, kiểu tĩnh an toàn, xử lý client, … tuy nhiên chừng này thông tin trong bài viết cũng đủ hình dung những ưu điểm của LINQ so với SQL. Đến đây, chắc hẳn nhiều bạn cho rằng nên vứt bỏ SQL. Hãy bình tĩnh! Thế giới công nghệ lúc nào cũng có 2 mặt, các bài tiếp theo tôi sẽ cho các bạn biết ưu điểm của SQL mà dù lỗi thời các công ty phần mềm buộc phải sử dụng.

Bình luận Facebook

Để lại bình luận

1 Comment on "Tại sao LINQ lại “đánh bại” SQL?"

Notify of
avatar
1000
Sort by:   newest | oldest | most voted
Huy
Guest

LINQ tốt nè, truy vấn gọn hơn, nhưng hiệu suất chưa chắc ăn SQL