Il modo più rapido e semplice per stampare "tutti i numeri primi (1-100)" è quello di abbracciare completamente il fatto che i numeri primi sono un insieme di valori noti, finiti e immutabili ("conosciuti" e "finiti" all'interno di un gamma particolare, ovviamente). Su questa scala ridotta, perché sprecare CPU ogni volta per calcolare un mucchio di valori che sono noti da molto tempo e non occupare quasi memoria per archiviare?
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)
Naturalmente, se è necessario calcolare i numeri primi tra 1 e 100, il seguente è abbastanza efficiente:
;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
);
Questa query verifica solo numeri dispari in quanto i numeri pari non saranno comunque primi. È anche specifico per l'intervallo 1 - 100.
Ora, se hai bisogno di un intervallo dinamico (simile a quello mostrato nel codice di esempio nella domanda), il seguente è un adattamento della query sopra che è ancora piuttosto efficiente (ha calcolato l'intervallo di 1 - 100.000 - 9592 voci - in poco meno di 1 secondo):
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
);
Il mio test (utilizzo SET STATISTICS TIME, IO ON;
) mostra che questa query ha prestazioni migliori rispetto alle altre due risposte fornite (finora):
GAMMA: 1 - 100
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 0
Dan 396 0 0
Martin 394 0 1
GAMMA: 1 - 10.000
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 47 170
Dan 77015 2547 2559
Martin n/a
GAMMA: 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
GAMMA: 99.900 - 100.000
NOTA : per eseguire questo test ho dovuto correggere un bug nel codice di Dan - @startnum
non è stato preso in considerazione nella query, quindi è sempre iniziato a 1
. Ho sostituito la Dividend.num <= @endnum
linea con Dividend.num BETWEEN @startnum AND @endnum
.
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 1
Dan 0 157 158
Martin n/a
GAMMA: 1 - 100.000 (test parziale parziale)
Dopo aver corretto la query di Dan per il test da 99.900 a 100.000, ho notato che non c'erano più letture logiche elencate. Quindi ho riprovato questo intervallo con quella correzione ancora applicata e ho scoperto che le letture logiche erano di nuovo sparite e i tempi erano leggermente migliori (e sì, lo stesso numero di righe è stato restituito).
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Dan 0 179,594 180,096