De longe, a maneira mais rápida e fácil de imprimir "todos os números primos (1-100)" é abraçar completamente o fato de que os números primos são um conjunto de valores conhecido, finito e imutável ("conhecido" e "finito" dentro de um faixa específica, é claro). Nesta pequena escala, por que desperdiçar CPU a cada vez para calcular um monte de valores conhecidos há muito tempo e ocupar quase nenhuma memória para armazenar?
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)
Obviamente, se você precisar calcular os números primos entre 1 e 100, o seguinte é bastante eficiente:
;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
);
Esta consulta apenas testa números ímpares, pois os números pares não serão primos de qualquer maneira. Também é específico para o intervalo de 1 a 100.
Agora, se você precisar de um intervalo dinâmico (semelhante ao mostrado no código de exemplo da pergunta), a seguir é apresentada uma adaptação da consulta acima que ainda é bastante eficiente (calculou o intervalo de 1 - 100.000 - 9592 entradas - em menos de 1 segundo):
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
);
Meu teste (usando SET STATISTICS TIME, IO ON;
) mostra que esta consulta tem um desempenho melhor do que as outras duas respostas fornecidas (até agora):
GAMA: 1-100
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 0
Dan 396 0 0
Martin 394 0 1
GAMA: 1 - 10.000
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 47 170
Dan 77015 2547 2559
Martin n/a
GAMA: 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
GAMA: 99.900 - 100.000
NOTA : Para executar este teste, eu tive que corrigir um erro no código de Dan - @startnum
não era fatorado na consulta, portanto, sempre começava às 1
. Troquei a Dividend.num <= @endnum
linha por Dividend.num BETWEEN @startnum AND @endnum
.
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 1
Dan 0 157 158
Martin n/a
GAMA: 1 - 100.000 (reteste parcial)
Depois de corrigir a consulta de Dan para o teste 99.900 - 100.000, notei que não havia mais leituras lógicas listadas. Portanto, retestei esse intervalo com essa correção ainda aplicada e descobri que as leituras lógicas haviam desaparecido novamente e os tempos eram um pouco melhores (e sim, o mesmo número de linhas foi retornado).
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Dan 0 179,594 180,096