Cho đến nay, cách nhanh nhất và dễ nhất để in "tất cả các số nguyên tố (1-100)" là nắm bắt hoàn toàn thực tế rằng các số nguyên tố là một tập hợp các giá trị đã biết, hữu hạn và không thay đổi ("đã biết" và "hữu hạn" trong một phạm vi cụ thể, tất nhiên). Ở quy mô nhỏ này, tại sao mỗi lần lãng phí CPU để tính toán một loạt các giá trị đã được biết đến từ rất lâu và hầu như không chiếm bất kỳ bộ nhớ nào để lưu trữ?
SELECT tmp.[Prime]
FROM (VALUES (2), (3), (5), (7), (11), (13),
(17), (19), (23), (29), (31), (37), (41),
(43), (47), (53), (59), (61), (67), (71),
(73), (79), (83), (89), (97)) tmp(Prime)
Tất nhiên, nếu bạn cần tính các số nguyên tố trong khoảng từ 1 đến 100, thì những điều sau đây khá hiệu quả:
;WITH base AS
(
SELECT tmp.dummy, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS [num]
FROM (VALUES (0), (0), (0), (0), (0), (0), (0)) tmp(dummy)
), nums AS
(
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT 1)) * 2) + 1 AS [num]
FROM base b1
CROSS JOIN base b2
), divs AS
(
SELECT [num]
FROM base b3
WHERE b3.[num] > 4
AND b3.[num] % 2 <> 0
AND b3.[num] % 3 <> 0
)
SELECT given.[num] AS [Prime]
FROM (VALUES (2), (3)) given(num)
UNION ALL
SELECT n.[num] AS [Prime]
FROM nums n
WHERE n.[num] % 3 <> 0
AND NOT EXISTS (SELECT *
FROM divs d
WHERE d.[num] <> n.[num]
AND n.[num] % d.[num] = 0
);
Truy vấn này chỉ kiểm tra các số lẻ vì dù sao số chẵn sẽ không là số nguyên tố. Nó cũng đặc trưng cho phạm vi 1 - 100.
Bây giờ, nếu bạn cần một phạm vi động (tương tự như được hiển thị trong mã ví dụ trong câu hỏi), thì sau đây là một điều chỉnh của truy vấn ở trên vẫn khá hiệu quả (nó đã tính phạm vi từ 1 - 100.000 - 9592 các mục - chỉ trong chưa đầy 1 giây):
DECLARE @RangeStart INT = 1,
@RangeEnd INT = 100000;
DECLARE @HowMany INT = CEILING((@RangeEnd - @RangeStart + 1) / 2.0);
;WITH frst AS
(
SELECT tmp.thing1
FROM (VALUES (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) tmp(thing1)
), scnd AS
(
SELECT 0 AS [thing2]
FROM frst t1
CROSS JOIN frst t2
CROSS JOIN frst t3
), base AS
(
SELECT TOP( CONVERT( INT, CEILING(SQRT(@RangeEnd)) ) )
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS [num]
FROM scnd s1
CROSS JOIN scnd s2
), nums AS
(
SELECT TOP (@HowMany)
(ROW_NUMBER() OVER (ORDER BY (SELECT 1)) * 2) +
(@RangeStart - 1 - (@RangeStart%2)) AS [num]
FROM base b1
CROSS JOIN base b2
), divs AS
(
SELECT [num]
FROM base b3
WHERE b3.[num] > 4
AND b3.[num] % 2 <> 0
AND b3.[num] % 3 <> 0
)
SELECT given.[num] AS [Prime]
FROM (VALUES (2), (3)) given(num)
WHERE given.[num] >= @RangeStart
UNION ALL
SELECT n.[num] AS [Prime]
FROM nums n
WHERE n.[num] BETWEEN 5 AND @RangeEnd
AND n.[num] % 3 <> 0
AND NOT EXISTS (SELECT *
FROM divs d
WHERE d.[num] <> n.[num]
AND n.[num] % d.[num] = 0
);
Thử nghiệm của tôi (sử dụng SET STATISTICS TIME, IO ON;
) cho thấy truy vấn này thực hiện tốt hơn hai câu trả lời còn lại (cho đến nay):
THAY ĐỔI: 1 - 100
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 0
Dan 396 0 0
Martin 394 0 1
THAY ĐỔI: 1 - 10.000
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 47 170
Dan 77015 2547 2559
Martin n/a
THAY ĐỔI: 1 - 100.000
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 984 996
Dan 3,365,469 195,766 196,650
Martin n/a
THAY ĐỔI: 99.900 - 100.000
LƯU Ý : Để chạy thử nghiệm này, tôi đã phải sửa một lỗi trong mã của Dan - @startnum
không được đưa vào truy vấn để nó luôn bắt đầu 1
. Tôi thay Dividend.num <= @endnum
dòng bằng Dividend.num BETWEEN @startnum AND @endnum
.
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 1
Dan 0 157 158
Martin n/a
RANGE: 1 - 100.000 (kiểm tra lại một phần)
Sau khi sửa câu hỏi của Dan cho bài kiểm tra 99.900 - 100.000, tôi nhận thấy rằng không có bài đọc logic nào được liệt kê. Vì vậy, tôi đã kiểm tra lại phạm vi này với bản sửa lỗi đó vẫn được áp dụng và thấy rằng các lần đọc logic đã biến mất và thời gian tốt hơn một chút (và vâng, cùng một số hàng đã được trả về).
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Dan 0 179,594 180,096