2009-03-15 29 views
39

Tôi không quan tâm đến nội dung của một hàng, tôi chỉ muốn biết một hàng có tồn tại không. Cột Name là khóa chính, do đó sẽ có 0 hoặc 1 hàng phù hợp. Hiện tại, tôi đang sử dụng:Cách nhanh nhất để xác định xem một hàng có tồn tại bằng cách sử dụng LINQ to SQL không?

if ((from u in dc.Users where u.Name == name select u).Count() > 0) 
    // row exists 
else 
    // row doesn't exist 

Trong khi các công trình trên, có rất nhiều công việc không cần thiết bằng cách chọn tất cả nội dung của hàng (nếu có). Điều sau có tạo truy vấn nhanh hơn không:

if (dc.Users.Where(u => u.Name == name).Any()) 

... hoặc có truy vấn nhanh hơn nữa không?

Trả lời

73

Cách tiếp cận Count() có thể thực hiện thêm công việc, vì (trong TSQL) EXISTS hoặc TOP 1 thường nhanh hơn nhiều; db có thể tối ưu hóa "là có ít nhất một hàng". Cá nhân, tôi sẽ sử dụng sự quá tải bất kỳ/ngữ:

if (dc.Users.Any(u => u.Name == name)) {...} 

Tất nhiên, bạn có thể so sánh những gì mỗi người thực hiện bằng cách quan sát các TSQL:

dc.Log = Console.Out; 
+2

Bổ sung thêm cho bạn chỉ vì tôi chưa bao giờ nhận ra có tính năng ghi nhật ký SQL cho LINQ to Sql. Tôi đã phải chạy SQL Profiler toàn bộ thời gian này. – David

+7

@ David - thực sự. Tôi tiếp tục yêu cầu nhóm dữ liệu MS thêm nó cho EF ;-p –

+0

nếu bạn muốn ** sử dụng Người dùng sau đó ** thay vì viết một truy vấn thứ hai vào cơ sở dữ liệu thì sao? kiểm tra [this] (http://stackoverflow.com/a/1071063/2218697), hy vọng sẽ giúp ai đó. – stom

2

Tôi nghĩ:

if (dc.Users.Any(u => u.Name == name)) {...} 

là cách tiếp cận tốt nhất.

0

Tôi không đồng ý rằng việc chọn đầu trang 1 sẽ luôn làm tốt hơn số lượng lựa chọn cho tất cả triển khai SQL. Tất cả phụ thuộc vào việc triển khai thực hiện, bạn biết đấy. Thật kỳ lạ, ngay cả bản chất của dữ liệu được lưu trữ trong một cơ sở dữ liệu cụ thể cũng ảnh hưởng đến kết quả tổng thể.

Hãy xem xét cả hai cách chúng tôi sẽ thực hiện chúng nếu tôi làm như vậy: Đối với cả hai trường hợp, đánh giá dự đoán (WHERE khoản) là một bước phổ biến.

Tiếp theo chọn đầu trang 1, bạn sẽ phải đọc tất cả các trường (trừ khi bạn chọn đầu 1 'x' ví dụ: chọn đầu 1 1). Điều này sẽ có chức năng tương đương với IQueryable.Any (...)., Ngoại trừ việc bạn sẽ dành một chút thời gian nhấp nháy trong giá trị cho mỗi cột của bản ghi được gặp đầu tiên nếu EXISTS. Nếu phát hiện SELECT TOP trong câu lệnh, phép chiếu sẽ bị cắt bớt nếu không có proc sau chiếu (ví dụ mệnh đề ORDER BY). Tiền xử lý này chịu một chi phí nhỏ nhưng đây là chi phí phụ trội nếu không có bản ghi nào tồn tại, trong trường hợp đó, một dự án đầy đủ vẫn được thực hiện.

Đối với số lượng đã chọn, quá trình xử lý trước không được thực hiện. Một phép chiếu được thực hiện và nếu EXISTS là sai, kết quả là ngay lập tức. Nếu EXISTS là true, số đếm vẫn nhanh vì nó sẽ chỉ là dW_Highest_Inclusive - dW_Lowest_Exclusive. Nhanh như 500 - 26. Nếu tồn tại là sai, kết quả thậm chí còn nhanh hơn.

Trường hợp còn lại do đó là: Tốc độ chiếu nhanh như thế nào và bạn làm gì bằng cách chiếu toàn bộ? Và câu trả lời dẫn đến vấn đề quan trọng nhất ở đây là: trường [NAME] có được lập chỉ mục hay không! Nếu bạn có chỉ mục trên [NAME], hiệu suất của một trong hai truy vấn sẽ gần đến mức nó sẽ thu hẹp tùy chọn của nhà phát triển.

Bằng và lớn, tôi sẽ chỉ viết hai đến bốn truy vấn LINQ và chênh lệch đầu ra trong thời gian trước và sau.

  1. select count
  2. chọn top 1
  3. chọn top 1 1
  4. chọn bất kỳ

Lặp lại tất cả 4 với một chỉ số nonclustered trên [TÊN];

8

Of Course

if (dc.Users.Where(u => u.Name == name).Any()) 

này là tốt nhất và nếu có nhiều điều kiện để kiểm tra sau đó nó rất đơn giản để viết như

Giả sử bạn muốn kiểm tra người sử dụng cho công ty sau đó

if (dc.Users.Where(u => u.ID== Id && u.Company==company).Any()) 
1

Đối với những người tuyên bố Any() là con đường phía trước tôi đã thực hiện một thử nghiệm đơn giản trong LinqPad so với một cơ sở dữ liệu SQL của CommonPasswords, 14 triệu cho hay lấy. Code:

var password = "qwertyuiop123"; 

var startTime = DateTime.Now; 
"From DB:".Dump(); 
startTime = DateTime.Now; 

if (CommonPasswords.Any(c => System.Data.Linq.SqlClient.SqlMethods.Like(c.Word, password))) 
{ 
    $"FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 
else 
{ 
    $"NOT FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 

"From DB:".Dump(); 
startTime = DateTime.Now; 
if (CommonPasswords.Where(c => System.Data.Linq.SqlClient.SqlMethods.Like(c.Word, password)).Count() > 0) 
{ 
    $"FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 
else 
{ 
    $"NOT FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 

"From DB:".Dump(); 
startTime = DateTime.Now; 
if (CommonPasswords.Where(c => c.Word.ToLower() == password).Take(1).Any()) 
{ 
    $"FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 
else 
{ 
    $"NOT FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump(); 
} 

Đây là SQL dịch:

-- Region Parameters 
DECLARE @p0 NVarChar(1000) = 'qwertyuiop123' 
-- EndRegion 
SELECT 
    (CASE 
     WHEN EXISTS(
      SELECT NULL AS [EMPTY] 
      FROM [Security].[CommonPasswords] AS [t0] 
      WHERE [t0].[Word] LIKE @p0 
      ) THEN 1 
     ELSE 0 
    END) AS [value] 
GO 

-- Region Parameters 
DECLARE @p0 NVarChar(1000) = 'qwertyuiop123' 
-- EndRegion 
SELECT COUNT(*) AS [value] 
FROM [Security].[CommonPasswords] AS [t0] 
WHERE [t0].[Word] LIKE @p0 
GO 

-- Region Parameters 
DECLARE @p0 NVarChar(1000) = 'qwertyuiop123' 
-- EndRegion 
SELECT 
    (CASE 
     WHEN EXISTS(
      SELECT NULL AS [EMPTY] 
      FROM (
       SELECT TOP (1) NULL AS [EMPTY] 
       FROM [Security].[CommonPasswords] AS [t0] 
       WHERE LOWER([t0].[Word]) = @p0 
       ) AS [t1] 
      ) THEN 1 
     ELSE 0 
    END) AS [value] 

Bạn có thể thấy rằng bất kỳ kết thúc tốt đẹp các truy vấn lên trong một lớp mã để làm một trường hợp tồn tại sau đó 1 nơi như Count() chỉ cần thêm vào một lệnh Count. Vấn đề với cả hai trong số này là bạn không thể làm một đầu (1) nhưng tôi không thể nhìn thấy một cách tốt hơn sử dụng hàng đầu (1)

Kết quả:

Từ DB: FOUND: thời gian xử lý: 13,3962

Từ DB: FOUND: thời gian xử lý: 12,0933

Từ DB: FOUND: thời gian xử lý: 787,8801

Again:

Từ DB: FOUND: thời gian xử lý: 13,3878

Từ DB: FOUND: thời gian xử lý: 12,6881

Từ DB: FOUND: thời gian xử lý: 780,2686

Again:

Từ DB: FOUND: thời gian xử lý: 24.7081

Từ DB: FOUND: thời gian xử lý: 23,6654

Từ DB: FOUND: thời gian xử lý: 699,622

Without Index:

Từ DB: FOUND: thời gian xử lý: 2395.1988

Từ DB: FOUND: thời gian xử lý: 390,6334

Từ DB: FOUND: thời gian xử lý: 664,8581

Bây giờ một số bạn có thể nghĩ nó chỉ là một hoặc hai phần nghìn giây. Tuy nhiên, sự chênh lệch lớn hơn nhiều trước khi tôi đặt chỉ mục lên đó; một vài giây.

Tính toán cuối cùng là khi tôi bắt đầu với khái niệm ToLower() sẽ nhanh hơn LIKE, và tôi đã đúng, cho đến khi tôi thử đếm và đặt chỉ mục trên đó. Tôi đoán Lower() làm cho chỉ mục không liên quan.

Các vấn đề liên quan