到目前为止,打印 “所有素数(1-100)” 的最快,最简单的方法是完全接受以下事实:素数是一组已知的,有限的且不变的值集(当然是特定范围)。在这么小的规模下,为什么每次都要浪费CPU来计算一堆已经很长时间知道的值,而几乎不占用任何内存来存储呢?
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-10,000
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
注意:为了运行此测试,我不得不修复Dan代码中的错误- @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(部分重新测试)
修正了Dan对99,900-100,000测试的查询后,我注意到这里没有列出更多的逻辑读物。因此,我在仍然应用此修复程序的情况下重新测试了该范围,发现逻辑读取再次消失,时间略有改善(是的,返回了相同数量的行)。
Query      Logical Reads       CPU Milliseconds    Elapsed Milliseconds
-------    ----------------    ----------------    -----------------
Dan                0             179,594            180,096