2013-09-03 59 views
5

Tôi có một bảng lớn với 100.000.000 hàng. Tôi muốn chọn mọi hàng thứ n từ bảng. bản năng đầu tiên của tôi là sử dụng một cái gì đó như thế này:Toán tử mô đun máy chủ SQL để chuyển đến mọi hàng thứ n trên một bảng lớn

SELECT id,name FROM table WHERE id%125000=0 

để lấy một thậm chí lây lan của 800 dòng (id là một chỉ số clustered)

Kỹ thuật này hoạt động tốt trên các tập dữ liệu nhỏ hơn nhưng với bảng lớn hơn của tôi truy vấn mất 2,5 phút. Tôi cho rằng điều này là do hoạt động của mô đun được áp dụng cho mọi hàng. Có phương pháp tối ưu nào hơn là bỏ qua hàng không?

+1

mục đích của việc này là gì? Bạn đang cố gắng để có được một tập hợp con ngẫu nhiên của kết quả? –

Trả lời

1

Nếu id là một chỉ số, sau đó tôi đang nghĩ đến việc gì đó dọc theo những dòng:

with ids as (
     select 1 as id 
     union all 
     select id + 125000 
     from ids 
     where id <= 100000000 
) 
select ids.id, 
     (select name from table t where t.id = ids.id) as name 
from ids 
option (MAXRECURSION 1000); 

Tôi nghĩ rằng việc xây dựng này sẽ sử dụng các chỉ số trên bàn.

EDIT:

Như tôi đã suy nghĩ về phương pháp này, bạn thực sự có thể sử dụng nó để có được id ngẫu nhiên thực tế trong bảng, chứ không phải là những người chỉ cách đều nhau:

with ids as (
     select 1 as cnt, 
      ABS(CONVERT(BIGINT,CONVERT(BINARY(8), NEWID()))) % 100000000 as id 
     union all 
     select cnt + 1, ABS(CONVERT(BIGINT,CONVERT(BINARY(8), NEWID()))) % 100000000 
     from ids 
     where cnt < 800 
) 

select ids.id, 
     (select name from table t where t.id = ids.id) as name 
from ids 
option (MAXRECURSION 1000); 

Mã cho thực tế trình tạo số ngẫu nhiên đến từ here.

EDIT:

Do quirks trong SQL Server, bạn vẫn có thể nhận được id không tiếp giáp, ngay cả trong kịch bản của bạn. Điều này được chấp nhận answer giải thích nguyên nhân. Tóm lại, các giá trị nhận dạng không được phân bổ một lần, mà là theo nhóm. Máy chủ có thể bị lỗi và thậm chí các giá trị không sử dụng bị bỏ qua.

Một lý do tôi muốn thực hiện lấy mẫu ngẫu nhiên là giúp tránh vấn đề này. Có lẽ, tình hình trên khá hiếm trên hầu hết các hệ thống. Bạn có thể sử dụng lấy mẫu ngẫu nhiên để tạo ra 900 id. Từ đó, bạn sẽ có thể tìm thấy 800 thực tế có sẵn cho mẫu của bạn.

+0

Tôi đang đánh dấu câu trả lời này là chính xác, nhưng để công bằng với những người đóng góp xuất sắc nhất khác, tôi nên thêm chi tiết hơn về trường hợp sử dụng của tôi vào câu hỏi. Bảng được tạo từ trình ghi dữ liệu. Giá trị id được đảm bảo tiếp giáp vì bảng sẽ vẫn tĩnh. Lý do tại sao câu trả lời của Gordon phù hợp với đơn đăng ký của tôi là tôi đang cố gắng tạo một biểu đồ tổng quan về dữ liệu, vì vậy việc sử dụng một mẫu ngẫu nhiên sẽ đủ tốt. Ngẫu nhiên tôi đã cố gắng TABLESAMPLE mà nên trong lý thuyết sản xuất cùng một kết quả, nhưng trên SQL SERVER 2012 ít nhất tôi không thấy một lựa chọn ngẫu nhiên thực sự. – Brian

2

Thời gian không đi vào hoạt động của mô đun, mà chỉ đọc 124,999 hàng không cần thiết cho mỗi hàng mà bạn thực sự muốn (ví dụ: Quét bảng hoặc Quét chỉ mục nhóm).

Chỉ là cách duy nhất để tăng tốc truy vấn như thế này là điều đầu tiên không hợp lệ: Thêm chỉ mục không được nhóm lên chỉ trên cột đó ([ID]). Ngoài ra, bạn có thể phải thêm một Chỉ mục Gợi ý để buộc nó sử dụng chỉ mục đó. Và cuối cùng, nó có thể không thực sự làm cho nó nhanh hơn, mặc dù cho một modulus 125,000+, nó nên được (mặc dù nó sẽ không bao giờ thực sự nhanh chóng).


Nếu ID của bạn không nhất thiết phải tiếp giáp (bất kỳ hàng bị xóa sẽ khá nhiều nguyên nhân này) và bạn thực sự cần chính xác mỗi hàng modulo, theo lệnh ID, sau đó bạn vẫn có thể sử dụng phương pháp trên, nhưng bạn sẽ phải xác định lại các ID cho hoạt động Modulo bằng cách sử dụng ROW_NUMBER() OVER(ORDER BY ID) trong truy vấn.

+0

Sẽ rất tuyệt vời nếu các chỉ mục được lọc hỗ trợ toán tử modulo, hoặc cột được tính toán để tính toán modulo cho bạn có thể được sử dụng. –

+0

@AaronBertrand Vâng, tất nhiên bạn cũng có thể đặt thêm cột theo cách thủ công để tạo điều kiện cho cùng một hiệu ứng. – RBarryYoung

2

Truy vấn của bạn giả định rằng các ID tiếp giáp nhau (và có thể chúng không có mà bạn không nhận ra điều này ...). Dù sao, bạn nên tự tạo ID:

select * 
from T 
where ID in (0, 250000*1, 250000*2, ...) 

Có thể bạn cần TVP để gửi tất cả ID vì có quá nhiều ID. Hoặc, bạn tạo ra các ID trên máy chủ trong T-SQL hoặc một hàm SQLCLR hoặc một bảng số.

Kỹ thuật này cho phép bạn thực hiện tìm kiếm chỉ mục và sẽ nhanh nhất bạn có thể sản xuất. Nó đọc số lượng dữ liệu tối thiểu có thể.

Modulo không phải là SARGable. SQL Server có thể hỗ trợ điều này nếu Microsoft muốn nó, nhưng đây là một trường hợp sử dụng kỳ lạ. Họ sẽ không bao giờ làm modulo SARGable và họ không nên.

+0

Tôi không biết về ý nghĩa hiệu suất, nhưng bạn có thể tham gia bên trong với CTE này thay vì sử dụng IN (...) 'với NumberRange (Số) là ( chọn 125000 là số union all chọn Số + 125000 từ NumberRange nơi Số <(125000 * 800) ) select * from NumberRange tùy chọn (maxrecursion 800) ' –

+0

tôi đã tự hỏi nếu có ai đó sẽ đề cập đến vấn đề ID không tiếp giáp ... :-) – RBarryYoung

+0

các vấn đề là nó có thể có thể bỏ lỡ nhiều trong số những hàng đó bởi vì không phải tất cả các giá trị ID đều được điền. –

0
DECLARE @i int, @max int, @query VARCHAR(1000) 
SET @i = 0 
SET @max = (SELECT max(id)/125000 FROM Table1) 
SET @query = 'SELECT id, name FROM Table1 WHERE id in (' 
WHILE @i <= @max 
BEGIN 
    IF @i > 0 SET @query = @query + ',' 
    SET @query = @query + CAST(@i*125000 as varchar(12)) 
    SET @i = @i + 1 
END 
SET @query = @query + ')' 
EXEC(@query) 

EDIT:

Để tránh bất kỳ "lỗ hổng" trong một tình huống ID không tiếp giáp, bạn có thể thử một cái gì đó như thế này:

DECLARE @i int, @start int, @id int, @max int, @query VARCHAR(1000) 
SET @i = 0 
SET @max = (SELECT max(id)/125000 FROM Table1) 
SET @query = 'SELECT id, name FROM Table1 WHERE id in (' 
WHILE @i <= @max 
BEGIN 
    SET @start = @i*125000 
    SET @id = (SELECT TOP 1 id FROM Table1 WHERE id >= @start ORDER BY id ASC) 
    IF @i > 0 SET @query = @query + ',' 
    SET @query = @query + CAST(@id as VARCHAR(12)) 
    SET @i = @i + 1 
END 
SET @query = @query + ')' 
EXEC(@query)