2010-06-14 34 views
8

Tôi tò mò về cách chính xác LINQ (không LINQ to SQL) đang thực hiện là tham gia phía sau hậu trường liên quan đến cách Sql Server thực hiện kết nối.LINQ tham gia - Hiệu suất

Sql Server trước khi thực hiện truy vấn, tạo ra một kế hoạch thực thi. Kế hoạch thực hiện về cơ bản là một cây biểu hiện trên những gì nó tin là cách tốt nhất để thực hiện truy vấn. Mỗi nút cung cấp thông tin về việc có nên thực hiện Sắp xếp, Quét, Chọn, Tham gia, vv.

Trên nút 'Tham gia' trong kế hoạch thực hiện của chúng tôi, chúng tôi có thể thấy ba thuật toán có thể có; Tham gia Hash, Merge Join và Nested Loops Tham gia. Sql Server sẽ chọn thuật toán cho mỗi phép nối tham gia dựa trên số hàng dự kiến ​​trong bảng bên trong và bên ngoài, loại tham gia mà chúng ta đang làm (một số thuật toán không hỗ trợ tất cả các kiểu kết nối), cho dù chúng ta cần dữ liệu có lẽ nhiều yếu tố khác.

Tham Algorithms:

Nested Vòng Tham gia: tốt nhất cho đầu vào nhỏ, có thể được tối ưu hóa với bảng bên đặt hàng.

Hợp nhất Tham gia: Tốt nhất cho đầu vào trung bình đến lớn được sắp xếp đầu vào hoặc đầu ra cần được đặt hàng.

Hash Tham gia: Tốt nhất cho các đầu vào từ trung bình đến lớn, có thể song song với quy mô tuyến tính.

LINQ Query:

DataTable firstTable, secondTable; 

... 

var rows = from firstRow in firstTable.AsEnumerable() 
       join secondRow in secondTable.AsEnumerable() 
        on firstRow.Field<object> (randomObject.Property) 
        equals secondRow.Field<object> (randomObject.Property) 
      select new {firstRow, secondRow}; 

SQL Query:

SELECT * 
FROM firstTable fT 
    INNER JOIN secondTable sT ON fT.Property = sT.Property 

Sql Server có thể sử dụng một Nested Vòng Tham gia nếu nó biết có một số ít hàng từ mỗi bảng, một hợp nhất tham gia nếu nó biết một trong các bảng có chỉ mục và Hash tham gia nếu nó biết có rất nhiều hàng trên một trong hai bảng và không có chỉ mục.

LINQ có chọn thuật toán của nó để tham gia không? hay nó luôn luôn sử dụng nó?

+0

+1 - thực hiện kế hoạch cai trị, và chỉ bằng cách biết về họ đặt bạn dễ dàng trong top 5% của các lập trình viên cơ sở dữ liệu. –

+2

Tôi đánh giá cao lời khen, nhưng bạn nghiêm túc đánh giá cao kiến ​​thức của tôi. – Meiscooldude

Trả lời

3

LINQ to SQL không gửi các gợi ý tham gia vào máy chủ. Do đó hiệu suất của phép nối bằng cách sử dụng LINQ to SQL sẽ giống với hiệu suất của cùng một kết nối được gửi "trực tiếp" tới máy chủ (tức là sử dụng ADO hoặc SQL Server Management Studio thuần túy) mà không có bất kỳ gợi ý nào được chỉ định.

LINQ to SQL cũng không cho phép bạn sử dụng các gợi ý tham gia (theo như tôi biết). Vì vậy, nếu bạn muốn ép buộc một kiểu tham gia cụ thể, bạn sẽ phải thực hiện nó bằng cách sử dụng một thủ tục được lưu trữ hoặc phương thức Execute[Command|Query]. Nhưng trừ khi bạn chỉ định kiểu kết nối bằng cách viết INNER [HASH|LOOP|MERGE] JOIN, thì SQL Server luôn chọn loại tham gia mà nó cho là hiệu quả nhất - không quan trọng truy vấn đến từ đâu.

Nhà cung cấp truy vấn LINQ khác - chẳng hạn như Khuôn khổ thực thể và LINiber NHibernate - sẽ thực hiện chính xác điều tương tự như LINQ to SQL. Không ai trong số này có bất kỳ kiến ​​thức trực tiếp về cách bạn đã lập chỉ mục cơ sở dữ liệu của bạn và do đó không ai trong số họ gửi gợi ý tham gia.

LINQ to Objects có một chút khác biệt - nó sẽ (hầu như luôn luôn) luôn thực hiện "băm nối" trong ngôn ngữ SQL Server. Đó là bởi vì nó thiếu các chỉ mục cần thiết để thực hiện một phép nối hợp nhất, và các phép nối băm là thường là hiệu quả hơn các vòng lồng nhau, trừ khi số phần tử rất nhỏ.Nhưng việc xác định số lượng các phần tử trong một IEnumerable<T> có thể yêu cầu lặp lại đầy đủ ngay từ đầu, vì vậy trong hầu hết các trường hợp, nó nhanh hơn chỉ để giả định điều tồi tệ nhất và sử dụng một thuật toán băm.

1

Chính LINQ không chọn thuật toán dưới dạng LINQ, nói một cách đơn giản, chỉ đơn giản là cách thể hiện truy vấn theo cú pháp giống SQL có thể ánh xạ để thực hiện các cuộc gọi trên IEnumerable<T> hoặc IQueryable<T>. LINQ là hoàn toàn tính năng ngôn ngữ và không cung cấp chức năng, chỉ là một cách khác để thể hiện các cuộc gọi hàm hiện có.

Trong trường hợp IQueryable<T>, hoàn toàn tùy thuộc vào nhà cung cấp (chẳng hạn như LINQ to SQL) để chọn phương pháp sản xuất kết quả tốt nhất.

Trong trường hợp LINQ to Objects (sử dụng IEnumerable<T>), liệt kê đơn giản là những gì được sử dụng (tương đương với vòng lồng nhau) trong mọi trường hợp. Không có kiểm tra sâu (hoặc thậm chí là kiến ​​thức về) các kiểu dữ liệu cơ bản để tối ưu hóa truy vấn.

+4

Điều này thực sự không hoàn toàn chính xác - LINQ to Objects 'JoinIterator' sử dụng một' Lookup 'bên trong, gần hơn với một phép nối băm. Mặc dù vì một lý do nào đó mà họ cho rằng nó thực sự là một vòng lặp lồng nhau trong LINQ to XML] (http://msdn.microsoft.com/en-us/library/bb387080.aspx). – Aaronaught

6

Phương pháp trên System.Linq.Enumerable được thực hiện theo thứ tự chúng được phát hành. Không có trình tối ưu hóa truy vấn nào khi phát.

Nhiều phương pháp rất lười, cho phép bạn không liệt kê đầy đủ nguồn bằng cách đặt .First hoặc .Any hoặc .Take ở cuối truy vấn. Đó là cách tối ưu hóa dễ nhất.

Đối với System.Linq.Enumerable.Jinin cụ thể, the docs tuyên bố rằng đây là lần tham gia băm.

Trình so sánh bình đẳng mặc định, Mặc định, được sử dụng để băm và so sánh các khóa.

Vì vậy, ví dụ:

//hash join (n+m) Enumerable.Join 
from a in theAs 
join b in theBs on a.prop equals b.prop 

//nestedloop join (n*m) Enumerable.SelectMany 
from a in theAs 
from b in theBs 
where a.prop == b.prop 
Các vấn đề liên quan