So sánh tốc độ linq và procedure

SubSonic do Rob Conery viết cách đây từ năm 2007 dựa trên pattern Active Record. nHibernate cũng tương tự. Hai thư viện này đều được gọi là ORM, Object Relation Mapping. Mình từng thử học nHibernate thấy nó khá loằng ngoằng vì phải dùng file XML để ánh xạ bảng, hoặc kết nối bảng của CSDL với các đối tượng tập collection trong .NET.

Ngược lại SubSonic thì đơn giản hơn, nó có một tool quét tất cả các bảng, stored procedure trong CSDL chủ yếu ở đây là SQL 2005 trở lên, Oracle cũng hỗ trợ nhưng mình chưa thử. Sau có thông tin đầy đủ về tên bảng, tên cột, kiểu dữ liệu của cột thì SubSonic sẽ sinh mã C# định nghĩa các collection chứa class, trong class có các trường thành viên tương ứng với cột. SubSonic có thể mô tả được cả quan hệ một-nhiều. NHibernate cũng vậy. Đối với stored procedure, thì SubSonic sinh mã là các hàm static. Khi lập trình viên CSDL thay đổi cấu trúc bảng, sửa, thêm mới stored procedure thì tool command line của SubSonic cần chạy lại để cập nhật ánh xạ. Ngoài ra lập trình viên có thể sử dụng partial class để thêm mới các method cho các class tương ứng với bảng trong CSDL. Mình rất thích tính năng này của SubSonic bởi không phải lo truyền sai tham số vào stored procedure. Về tốc độ, mình nghĩ SubSonic và nHibernate không khác nhau nhiều. Nhưng về dễ sử dụng thì mình thích SubSonic hơn. LINQ thì có nhiều điểm cải tiến hơn là ORM thuần tuý. LINQ có thể truy vấn cho .NET collection, XML, và SQL,… Căn bản bên trong của LINQ tận dụng những cú pháp mới trong C# 3.0: - Hàm mở rộng, extension method, gắn hàm mới vào đối tượng kể cả kiểu cơ bản như int mà không cần định nghĩa hàm thừa kế. Hàm này phải khai báo là pubic static nhé - Biểu thức Lamba, lambda expression, cách viết mới của delegate inline function. Tốc độ thì cả 3 công nghệ trên khi đo đạc cá nhân sử dụng kiến trúc client-server đều không thể nhanh bằng viết lời gọi đến stored procedure. Tuy nhiên, khả năng mở rộng, phân tán các tập đối tượng, objection collection giữa các máy tính trong kiến trúc SOA rõ ràng là dễ dàng hơn so với Client Server ngày xưa. Hơn nữa khi nói đến tốc độ, chúng ta phải bàn đến cả khả năng chịu tải truy cập, load balancing & scalability. Một câu lệnh SQL chạy rất nhanh khi chỉ có duy nhất yêu cầu query thì chưa chắc đã đủ. Thực tế nhiều khi là hàng nghìn yêu cầu query đến cùng một lúc. Lúc này, cần phải tính tới phân tải database server hoặc cache dữ liệu trên nhiều application logic tier trung gian. Khi đó LINQ hay ActiveRecord sẽ phát huy sở trưởng của mình. Với LINQ chỉ cần học một cú pháp, có thể truy vấn nhiều nguồn dữ liệu khác nhau. Chúng ta có thể mở rộng cả cú pháp LINQ viết thêm các hàm deffered và non differed function mới khi chúng ta có kinh nghiệm với hàm mở rộng và biểu thức lamba. Riêng cái này thì ANSI SQL bó tay rồi.

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 Paginate [this IQueryable 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.

Chủ Đề