2014-11-11 14 views
7

Tôi có một câu hỏi SQL tiên tiến cho SQL của bạn Perf guru ngoài kia :-)SQL Server truy vấn - không hoạt động như mong đợi, không hành xử như tôi nghĩ rằng nó sẽ

Tôi hiện đang cố gắng tìm hiểu một số hành vi trong một ứng dụng lớn hơn, nhưng nó nắm tới một truy vấn đối với hai bảng sau đây:

  • Users bảng - khoảng 750 mục, UserId (varchar(50)) như nhóm PK
  • ActionLog bảng - hàng triệu mục, bao gồm UserId - nhưng không có mối quan hệ FK

Đối với lưới trong ứng dụng ASP.NET của tôi, tôi đang cố gắng để có được tất cả người dùng cộng với ngày nhập nhật ký cuối cùng của họ.

Các câu lệnh SQL mà hiện đang được sử dụng trông giống như sau:

SELECT 
    UserId, (other columns), 
    LastLogDate = (SELECT TOP (1) [Timestamp] FROM dbo.ActionLog a WHERE a.UserId = u.UserId ORDER BY [Timestamp] DESC) 
FROM 
    dbo.Users u; 

và nó sẽ trả về các hàng để hiển thị - nhưng nó khá chậm (khoảng 20 giây.).

Suy nghĩ đầu tiên của tôi là để thêm một chỉ mục trên bảng ActionLog trên UserId và để bao gồm các Timestamp cột trong nó:

CREATE NONCLUSTERED INDEX [IDX_UserId] 
ON [dbo].[ActionLog]([UserId] ASC) 
INCLUDE ([Timestamp]) 

Các hàng đang trở lại rất nhanh - dưới 2 giây, với 350'000 các mục nhập trong bảng ActionLog và chỉ mục của tôi đang được sử dụng tốt, vì kế hoạch thực hiện hiển thị cho tôi. Tất cả có vẻ ổn. Bây giờ, để xấp xỉ kịch bản sản xuất, chúng tôi đã tải khoảng 2 triệu hàng vào bảng ActionLog, 95% hoặc hơn trong số đó đề cập đến người dùng không tồn tại (tức là các hàng này có một số UserId không tồn tại trong Users bảng).

Bỗng nhiên, truy vấn trở thành cực kỳ chậm (24 phút!) Và chỉ mục không còn được sử dụng nữa.

Tôi cho rằng kể từ khi đại đa số các mục trong bảng ActionLog không thẳng hàng với một người dùng hiện, tôi sẽ thấy tăng hiệu suất nếu tôi sử dụng một chỉ số lọc - để "lọc bỏ" tất cả những lộn xộn mục mà không có một người sử dụng tương ứng - vì vậy tôi tạo ra chỉ số này (thay thế một trong những khác đã tồn tại trước đó):

CREATE NONCLUSTERED INDEX [IDX_UserId] 
ON [dbo].[Log]([UserId] ASC) 
INCLUDE ([Timestamp]) 
WHERE UserId <> 'user' -- that's the fixed, non-existing "UserId" I wanted to avoid 

Nhưng để mất tinh thần của tôi - truy vấn vẫn về cùng là - mất hơn 20 phút để hoàn thành. Tôi cập nhật số liệu thống kê - không thay đổi - vẫn cực kỳ chậm.

Điều thú vị (đối với tôi) là: khi tôi xóa chỉ mục và tạo lại nó -> bây giờ truy vấn thực sự nhanh chóng trở lại (một lần nữa ít hơn 3 giây). WOW!

Nhưng ngay sau khi tôi bắt đầu thêm các mục nhập khác một lần nữa, truy vấn "nghiêng" và trở nên thực sự thực sự chậm .......

Tôi không hiểu tại sao điều này xảy ra - tôi đã nghĩ rằng với chỉ mục được lọc loại bỏ tất cả các mục "giả mạo" đó, tôi sẽ thấy hiệu suất tốt khi cố tìm mục nhập ActionLog mới nhất cho người dùng hiện tại - nhưng đó không phải là trường hợp.

TẠI SAO KHÔNG?

Bất kỳ ý tưởng nào? Suy nghĩ? Những điều cần thử ??

+0

Bạn có thể gửi hoặc mô tả kế hoạch thực hiện khi bạn thả và tạo lại chỉ mục so với khi bạn bắt đầu thêm nhiều mục nhập hơn và nó bị hỏng? Là nó chỉ quyết định không sử dụng chỉ mục sau khi hàng được thêm vào? – jimdrang

+1

Thử thêm biểu thức lọc vào truy vấn phụ tương quan của bạn 'a.UserId <> 'user''. Tôi nghĩ rằng trình tối ưu hóa cần nó để xem xét chỉ số đã lọc. –

+0

Có một số mẹo về cách tạo và sử dụng chỉ mục được lọc trên MSDN. Bạn có thể thử chỉ định 'WITH (INDEX (IDX_UserId))' ở cuối mệnh đề 'SELECT'. Tham khảo: [Tạo chỉ mục lọc] (http://msdn.microsoft.com/en-us/library/cc280372.aspx) –

Trả lời

3

Đầu tiên, INCLUDE đây không phải là lựa chọn tốt nhất. Bạn sắp xếp theo ngày nhập, nhưng các cột được bao gồm không được sắp xếp. Giải pháp tốt hơn sẽ là:

CREATE NONCLUSTERED INDEX [IX_ActionLog_UserIdTimestamp] ON [dbo].[ActionLog] 
([UserId], [Timestamp]); 

Thứ hai, có vẻ như bạn có thể cần phải cập nhật số liệu thống kê về chỉ mục của mình thường xuyên hơn cập nhật tự động. Tôi đã nhìn thấy trường hợp khi, trong một tình huống giống như của bạn, tôi đã phải cập nhật số liệu thống kê mỗi 10 phút, do chèn quá mức. Đó là trở lại vào năm 2005, mặc dù.

+0

Không chắc chắn lợi ích nào thêm 'Dấu thời gian' vào chính chỉ mục sẽ mang lại - vì đó là cột thứ hai trong chỉ mục, chỉ mục này sẽ không thể được sử dụng cho sắp xếp theo 'Timestamp' ..... (Tôi sẽ thử nó tại văn phòng sau này và báo cáo lại) .Vâng: cập nhật các số liệu thống kê dường như không giúp được :-((điều này hơi khó hiểu với tôi) –

+1

@marc_s, chỉ mục sẽ được sắp xếp theo UserId, sau đó (trong mỗi người dùng) bởi Dấu thời gian. Điều này sẽ cho phép bạn nhanh chóng vây d cuối cùng (tức là tối đa()) cho mỗi người trong số họ. Cột bao gồm không cung cấp một lợi ích như vậy, afair. –

+0

Roger - bạn hoàn toàn đúng. Chỉ số này dường như hoạt động tốt hơn so với những gì tôi có. Cảm ơn! –

-1

Erase các subselect:

SELECT u.UserId, Max(a.TimeStamp) As LastLogDate 
FROM dbo.Users u 
,  dob.ActionLog a 
Where a.UserId = u.UserId 
Group By u.UserId; 

Sau đó nghĩ về việc các cột khác.

+1

OK - nhưng sau đó tôi khuyên bạn không nên sử dụng danh sách các bảng được phân tách bằng dấu phẩy (kiểu này) đã không được chấp nhận với tiêu chuẩn SQL: 92 - hơn 20 năm trước!) - sử dụng ** cú pháp ** ANSI JOIN thích hợp! –

+0

địa ngục, vâng, nhưng vì tôi buộc phải sử dụng một số crap cũ trong công việc có PROBS cú pháp prober JOIN, shit sẽ xảy ra :(thanx! –

2

Hãy thử truy vấn này và xem cách nó thực hiện với chỉ số ban đầu của bạn hoặc với Modified được đề xuất bởi @Roger Wolf:

SELECT u.UserId, a.LastLogDate 
FROM dbo.Users u 
INNER JOIN (
    SELECT UserId, Max([TimeStamp]) AS LastLogDate 
    FROM dbo.ActionLog 
    WHERE userid <> 'user' -- the user to filter out 
    GROUP BY UserId 
) a ON a.UserId = u.UserId 

Nếu nó sucks tôi sẽ xóa câu trả lời :)

+0

Nó không hút gì cả - nó chỉ là tốt, nếu không tốt hơn, so với những gì Roger gợi ý. Hiệu suất ổn định và rất nhanh - cảm ơn! –

+0

@marc_s Ah ok, thật tuyệt khi biết. Rõ ràng là tôi không có một lượng lớn dữ liệu phù hợp để kiểm tra nó. Thật tốt khi biết nó hoạt động tốt. – jpw

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