Zdecydowanie najszybszym i najłatwiejszym sposobem na wydrukowanie „wszystkich liczb pierwszych (1-100)” jest pełne uwzględnienie faktu, że liczby pierwsze są znanym, skończonym i niezmiennym zestawem wartości („znanym” i „skończonym” w obrębie oczywiście określony zakres). Na tak małą skalę, po co marnować procesor za każdym razem, aby obliczyć wiązkę wartości, które są znane od bardzo dawna i nie zajmują prawie żadnej pamięci do przechowywania?
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)
Oczywiście, jeśli musisz obliczyć liczby pierwsze od 1 do 100, następujące działania są dość skuteczne:
;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
);
To zapytanie sprawdza tylko liczby nieparzyste, ponieważ liczby parzyste i tak nie będą pierwsze. Jest również specyficzny dla zakresu od 1 do 100.
Teraz, jeśli potrzebujesz zakresu dynamicznego (podobnego do tego, który pokazano w przykładowym kodzie w pytaniu), to poniżej znajduje się dostosowanie powyższego zapytania, które jest nadal dość wydajne (obliczyło zakres 1 - 100 000 - 9592 wpisy - w niecałe 1 sekundę):
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
);
Moje testowanie (używanie SET STATISTICS TIME, IO ON;) pokazuje, że to zapytanie działa lepiej niż pozostałe dwie odpowiedzi (jak dotąd):
ZAKRES: 1 - 100
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 0
Dan 396 0 0
Martin 394 0 1
ZAKRES: 1 - 10 000
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 47 170
Dan 77015 2547 2559
Martin n/a
ZAKRES: 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
ZAKRES: 99 900 - 100 000
UWAGA : Aby uruchomić ten test, musiałem naprawić błąd w kodzie Dana - @startnumnie został uwzględniony w zapytaniu, więc zawsze zaczynał się od 1. Zamieniłem Dividend.num <= @endnumlinię na Dividend.num BETWEEN @startnum AND @endnum.
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 1
Dan 0 157 158
Martin n/a
ZAKRES: 1 - 100 000 (częściowy ponowny test)
Po naprawieniu zapytania Dana dla testu 99,900 - 100 000 zauważyłem, że nie ma już żadnych logicznych odczytów. Ponownie przetestowałem ten zakres z wciąż stosowaną poprawką i stwierdziłem, że logiczne odczyty znowu zniknęły, a czasy były nieco lepsze (i tak, zwrócono tę samą liczbę wierszy).
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Dan 0 179,594 180,096