Безусловно, самый быстрый и простой способ вывести «все простые числа (1-100)» - это полностью принять тот факт, что простые числа являются известным, конечным и неизменным набором значений («известных» и «конечных» в пределах конкретный ассортимент, конечно). При таком небольшом масштабе зачем каждый раз тратить ЦП на вычисление набора значений, которые были известны очень давно, и почти не занимать память для хранения?
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)
Конечно, если вам нужно вычислить простые числа от 1 до 100, следующее довольно эффективно:
;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
);
Этот запрос проверяет только нечетные числа, так как четные числа в любом случае не будут простыми. Это также характерно для диапазона от 1 до 100.
Теперь, если вам нужен динамический диапазон (аналогичный тому, который показан в примере кода в вопросе), то приведенная ниже адаптация приведенного выше запроса все еще довольно эффективна (он рассчитал диапазон 1 - 100 000 - 9592 записи - менее чем за 1 секунду):
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
);
Мое тестирование (использование SET STATISTICS TIME, IO ON;) показывает, что этот запрос работает лучше, чем два других ответа (пока):
ДИАПАЗОН: 1 - 100
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 0
Dan 396 0 0
Martin 394 0 1
ДИАПАЗОН: 1 - 10000
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 47 170
Dan 77015 2547 2559
Martin n/a
ДИАПАЗОН: 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
ДИАПАЗОН: 99 900 - 100 000
ПРИМЕЧАНИЕ . Чтобы выполнить этот тест, мне пришлось исправить ошибку в коде Дэна - @startnumон не был включен в запрос, поэтому он всегда начинался с 1. Я заменил Dividend.num <= @endnumстроку с Dividend.num BETWEEN @startnum AND @endnum.
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 1
Dan 0 157 158
Martin n/a
ДИАПАЗОН: 1 - 100 000 (частичный повторный тест)
После исправления запроса Дэна для теста 99 900 - 100 000 я заметил, что в списке больше нет логических операций чтения. Поэтому я повторно протестировал этот диапазон с примененным исправлением и обнаружил, что логические чтения снова исчезли, и времена были немного лучше (и да, было возвращено то же количество строк).
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Dan 0 179,594 180,096