"모든 소수 (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 (부분 재시험)
99,900-100,000 테스트에 대한 Dan의 쿼리를 수정 한 후 더 이상 논리적 읽기가 나열되지 않음을 알았습니다. 그래서 나는이 수정 사항이 여전히 적용된 채로이 범위를 다시 테스트했으며 논리적 읽기가 다시 사라지고 시간이 약간 더 낫다는 것을 알았습니다 (예, 동일한 수의 행이 반환되었습니다).
Query Logical Reads CPU Milliseconds Elapsed Milliseconds
------- ---------------- ---------------- -----------------
Dan 0 179,594 180,096