2009-08-20 8 views
7

Tôi tự hỏi nếu có một truy vấn có hiệu suất tốt để chọn ngày riêng biệt (bỏ qua thời gian) từ một bảng có trường datetime trong SQL Server.Làm thế nào để nhanh chóng chọn ngày DISTINCT từ trường Ngày/Giờ, SQL Server

Sự cố của tôi không khiến máy chủ thực sự thực hiện việc này (tôi đã thấy this question rồi, và chúng tôi đã có một cái gì đó tương tự đã có sẵn bằng DISTINCT). Vấn đề là liệu có bất kỳ thủ thuật nào để thực hiện nó nhanh hơn hay không. Với dữ liệu chúng tôi đang sử dụng, truy vấn hiện tại của chúng tôi sẽ trả về ~ 80 ngày khác nhau có ~ 40.000 hàng dữ liệu (sau khi lọc trên cột được lập chỉ mục khác), có chỉ mục trên cột ngày và truy vấn luôn quản lý 5 giây trở lên. Đó là quá chậm.

Thay đổi cấu trúc cơ sở dữ liệu có thể là một tùy chọn, nhưng tùy chọn ít hơn.

Trả lời

6

Mọi tùy chọn liên quan đến thao tác CAST hoặc TRUNCATE hoặc DATEPART trên trường ngày giờ có cùng vấn đề: truy vấn phải quét toàn bộ resultset (40k) để tìm ngày riêng biệt. Hiệu suất có thể thay đổi nhỏ giữa các triển khai khác nhau.

Điều bạn thực sự cần là có chỉ mục có thể tạo phản hồi trong nháy mắt. Bạn có thể có cột được tính liên tục và chỉ mục (yêu cầu thay đổi cấu trúc bảng) hoặc chế độ xem được lập chỉ mục (requires Enterprise Edition for QO to consider the index ngoài hộp).

tồn tính cột:

alter table foo add date_only as convert(char(8), [datetimecolumn], 112) persisted; 
create index idx_foo_date_only on foo(date_only); 

Indexed xem:

create view v_foo_with_date_only 
with schemabinding as 
select id 
    , convert(char(8), [datetimecolumn], 112) as date_only 
from dbo.foo; 
create unique clustered index idx_v_foo on v_foo_with_date_only(date_only, id); 

Cập nhật

Để loại bỏ hoàn toàn một lần quét có thể sử dụng một GROUP BY lừa xem được lập chỉ mục, như thế này:

create view v_foo_with_date_only 
with schemabinding as 
select 
    convert(char(8), [d], 112) as date_only 
    , count_big(*) as [dummy] 
from dbo.foo 
group by convert(char(8), [d], 112) 

create unique clustered index idx_v_foo on v_foo_with_date_only(date_only) 

Truy vấn select distinct date_only from foo sẽ sử dụng chế độ xem được lập chỉ mục này thay thế. Vẫn là một kỹ thuật quét, nhưng trên một chỉ mục đã được phân biệt, vì vậy chỉ các bản ghi cần thiết mới được quét. Đó là một hack, tôi nghĩ, tôi sẽ không khuyên bạn nên nó cho mã sản xuất trực tiếp.

Máy chủ SQL AFAIK không có khả năng quét chỉ mục thực sự với các lần lặp lại, ví dụ: tìm kiếm hàng đầu, sau đó tìm kiếm lớn hơn đầu, sau đó tìm kiếm thành công lớn hơn tìm thấy cuối cùng.

+0

Có cách nào để sử dụng 'QUẢNG CÁO SKIP' trong' SQL Server' không? Tôi vừa thử giải pháp của bạn trên bảng '2M' và thậm chí còn tệ hơn (' DISTINCT CAST (...) 'trên trường' DATETIME' mất '850 ms' với' Hash Match Aggregate', 'DISTINCT date' mất '1800 ms' với' Tập hợp luồng '). 'Oracle' và' MySQL' sẽ chỉ nhảy qua các trường riêng biệt trong chỉ mục, 'SQL Server' không làm điều đó. – Quassnoi

+0

Bạn cần phải chọn date_only riêng biệt sau khi tạo chỉ mục trên đó. –

+0

'@ Remus': Tôi đã tạo chỉ mục và trình tối ưu hóa đã sử dụng nó. – Quassnoi

9

Tôi đã sử dụng như sau:

CAST(FLOOR(CAST(@date as FLOAT)) as DateTime); 

này loại bỏ thời gian từ ngày bằng cách chuyển đổi nó vào một float và cắt bỏ ra khỏi "thời gian" một phần, đó là số thập phân của float.

Có vẻ hơi clunky nhưng hoạt động tốt trên tập dữ liệu lớn (~ 100.000 hàng) tôi sử dụng nhiều lần trong ngày.

3

Cách đơn giản nhất là thêm cột được tính cho chỉ phần ngày và chọn ngày đó. Bạn có thể làm điều này trong một cái nhìn nếu bạn không muốn thay đổi bảng.

2

Cập nhật:

Giải pháp dưới đây được thử nghiệm cho hiệu quả trên một bảng 2M và mất nhưng 40 ms.

Đồng bằng DISTINCT trên cột được lập chỉ mục đã lấy 9 seconds.

Xem cụm từ này trong blog của tôi để biết chi tiết thực hiện:


Thật không may, SQL Server 'ưu s có thể làm không phải của SKIP SCAN Oracle cũng không MySQL' s INDEX FOR GROUP-BY.

Luôn luôn là Stream Aggregate mất nhiều thời gian.

Bạn có thể xây dựng một danh sách các ngày có thể sử dụng một đệ quy CTE và tham gia nó với bảng của bạn:

WITH rows AS (
     SELECT CAST(CAST(CAST(MIN(date) AS FLOAT) AS INTEGER) AS DATETIME) AS mindate, MAX(date) AS maxdate 
     FROM mytable 
     UNION ALL 
     SELECT mindate + 1, maxdate 
     FROM rows 
     WHERE mindate < maxdate 
     ) 
SELECT mindate 
FROM rows 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM mytable 
     WHERE date >= mindate 
       AND date < mindate + 1 
     ) 
OPTION (MAXRECURSION 0) 

này sẽ hiệu quả hơn Stream Aggregate

+0

Xây dựng một bảng cập nhật và sau đó bán tham gia với bản gốc là một giải pháp tuyệt vời. IMHO chi phí bổ sung của một cột liên tục với một chỉ mục hoặc một cái nhìn được lập chỉ mục chỉ có ý nghĩa nếu bạn phải thực hiện thao tác này rất thường xuyên (phỏng đoán tùy ý: giống như một vài trăm lần mỗi ngày). Tôi sẽ luôn luôn muốn đầu tiên cố gắng để tìm ra một truy vấn tốt hơn so với thêm phức tạp hơn/chi phí cho cấu trúc DB. –

0

Nếu bạn muốn tránh việc khai thác bước hoặc định dạng lại ngày tháng - có lẽ là nguyên nhân chính của sự trì hoãn (bằng cách bắt buộc quét toàn bộ bảng) - bạn không còn cách nào khác ngoài việc lưu trữ ngày chỉ một phần của datetime, mà không may sẽ yêu cầu thay đổi cấu trúc cơ sở dữ liệu.

Nếu bạn sử dụng SQL Server 2005 hay muộn sau đó một lĩnh vực tính toán tiếp tục tồn tại là con đường để đi

 
Unless otherwise specified, computed columns are virtual columns that are 
not physically stored in the table. Their values are recalculated every 
time they are referenced in a query. The Database Engine uses the PERSISTED 
keyword in the CREATE TABLE and ALTER TABLE statements to physically store 
computed columns in the table. Their values are updated when any columns 
that are part of their calculation change. By marking a computed column as 
PERSISTED, you can create an index on a computed column that is deterministic 
but not precise. 
+1

Nguyên nhân chính của sự chậm trễ là quét và sắp xếp để tạo ra sự khác biệt. Trừ khi một cái gì đó * extreamly * phức tạp xảy ra trong một hoạt động vô hướng, sự chậm trễ trong một cơ sở dữ liệu luôn luôn liên quan đến truy cập dữ liệu, không để hoạt động vô hướng. –

+0

Đó là nguyên nhân chính gây ra sự chậm trễ vì nó bắt buộc quét toàn bộ bảng - xin lỗi, nên đã làm rõ rằng – Cruachan

0

ngữ của bạn trên cột lọc khác là gì? Bạn đã thử xem bạn có được cải thiện từ chỉ mục trên cột được lọc khác không, tiếp theo là trường ngày giờ?

Tôi phần lớn đoán ở đây, nhưng 5 giây để lọc một tập hợp có lẽ 100000 hàng xuống còn 40000 và sau đó thực hiện sắp xếp (có lẽ là những gì diễn ra) dường như không phải là thời gian vô lý đối với tôi. Tại sao bạn nói nó quá chậm? Bởi vì nó không phù hợp với kỳ vọng?

3

Tôi không chắc chắn lý do truy vấn hiện tại của bạn sẽ chiếm hơn 5 s cho 40.000 hàng.

Tôi vừa thử truy vấn sau đối với một bảng có 100.000 hàng và nó được trả về dưới 0,1 giây.

SELECT DISTINCT DATEADD(day, 0, DATEDIFF(day, 0, your_date_column)) 
FROM your_table 

(Lưu ý rằng truy vấn này có thể không tận dụng được bất kỳ chỉ mục nào trên cột ngày tháng, giả sử bạn không thực hiện nó hàng chục lần mỗi giây.)

+0

dễ dàng và sạch sẽ, đây phải là câu trả lời đúng –

0

Chỉ cần chuyển đổi ngày: dateadd(dd,0, datediff(dd,0,[Some_Column]))

1

Tôi sử dụng này

SELECT 
DISTINCT DATE_FORMAT(your_date_column,'%Y-%m-%d') AS date 
FROM ... 
+0

Không chắc chắn về hiệu quả, nhưng đó chắc chắn là cách đẹp nhất để làm điều đó. – ylnor

5

này làm việc cho tôi:

SELECT distinct(CONVERT(varchar(10), {your date column}, 111)) 
FROM {your table name}