Der mit Abstand schnellste und einfachste Weg, "alle Primzahlen (1-100)" zu drucken, besteht darin, die Tatsache vollständig zu berücksichtigen, dass Primzahlen eine bekannte, endliche und unveränderliche Menge von Werten sind ("bekannt" und "endlich" innerhalb von a besondere Reichweite natürlich). Warum sollten Sie in diesem kleinen Maßstab jedes Mal CPU verschwenden, um eine Reihe von Werten zu berechnen, die seit langem bekannt sind, und kaum Speicherplatz zum Speichern benötigen?
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)
Wenn Sie die Primzahlen zwischen 1 und 100 berechnen müssen, ist Folgendes natürlich ziemlich effizient:
;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
);
Diese Abfrage testet nur ungerade Zahlen, da gerade Zahlen sowieso keine Primzahlen sind. Es ist auch spezifisch für den Bereich von 1 bis 100.
Wenn Sie nun einen Dynamikbereich benötigen (ähnlich wie im Beispielcode in der Frage gezeigt), ist das Folgende eine Anpassung der obigen Abfrage, die immer noch ziemlich effizient ist (sie hat den Bereich von 1 - 100.000 - 9592 berechnet Einträge - in knapp 1 Sekunde):
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
);
Meine Tests (mit SET STATISTICS TIME, IO ON;
) zeigen, dass diese Abfrage eine bessere Leistung erbringt als die beiden anderen Antworten (bisher):
BEREICH: 1 - 100
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 0
Dan 396 0 0
Martin 394 0 1
BEREICH: 1 - 10.000
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 47 170
Dan 77015 2547 2559
Martin n/a
BEREICH: 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
BEREICH: 99.900 - 100.000
HINWEIS : Um diesen Test auszuführen, musste ich einen Fehler in Dans Code beheben - @startnum
wurde in der Abfrage nicht berücksichtigt, sodass er immer bei begann 1
. Ich habe die Dividend.num <= @endnum
Leitung durch ersetzt Dividend.num BETWEEN @startnum AND @endnum
.
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Solomon 0 0 1
Dan 0 157 158
Martin n/a
BEREICH: 1 - 100.000 (teilweiser erneuter Test)
Nachdem ich Dans Abfrage für den 99.900 - 100.000 Test korrigiert hatte, stellte ich fest, dass keine logischen Lesevorgänge mehr aufgeführt waren. Also habe ich diesen Bereich erneut getestet, wobei der Fix noch angewendet wurde, und festgestellt, dass die logischen Lesevorgänge wieder weg waren und die Zeiten etwas besser waren (und ja, die gleiche Anzahl von Zeilen wurde zurückgegeben).
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Dan 0 179,594 180,096