用SQL計(jì)算100以內(nèi)的質(zhì)數(shù)
?
??? 以前寫過一篇文章,描述如何使用PL/SQL來計(jì)算100以內(nèi)的質(zhì)數(shù),今天重翻那篇文章的時候,突然想到,能不能用SQL來實(shí)現(xiàn)同樣的功能。
?
?
?
?
?
??? 其實(shí)這個功能用PLSQL實(shí)現(xiàn)最簡單,思路也很清晰,判斷一個數(shù)是否是質(zhì)數(shù),只需要檢查這個數(shù)能否被比它小的質(zhì)數(shù)整除就可以了。但是這種操作通過SQL來實(shí)現(xiàn)就十分的困難,因此這里通過另外一種方式來實(shí)現(xiàn)這個功能——構(gòu)造。
?
通過查詢100以內(nèi)的數(shù),去掉所有的合數(shù),剩下的就是質(zhì)數(shù)了:
?
SQL> WITH T?
??2?AS?
? 3?(SELECT ROWNUM RN FROM DUAL CONNECT BY LEVEL < 100)
? 4?SELECT RN FROM T?
? 5?WHERE RN > 1
? 6?MINUS
? 7?SELECT A.RN * B.RN FROM T A, T B
? 8?WHERE A.RN <= B.RN
? 9?AND A.RN > 1
?10?AND B.RN > 1;
?
??????? RN
----------
???????? 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
?
25?rows selected.
?
?
?
但是這種方法的效率明顯要比PL/SQL的效率要低,如果取10000以內(nèi)的質(zhì)數(shù):
?
?
SQL> SET TIMING ON
SQL> SET AUTOT TRACE STAT
SQL> WITH T?
? 2?AS?
? 3?(SELECT ROWNUM RN FROM DUAL CONNECT BY LEVEL < 10000)
? 4?SELECT RN FROM T?
? 5?WHERE RN > 1
? 6?MINUS
? 7?SELECT A.RN * B.RN FROM T A, T B
? 8?WHERE A.RN <= B.RN
? 9?AND A.RN > 1
?10?AND B.RN > 1;
?
1229 rows selected.
?
Elapsed: 00:02:41.54
?
Statistics
----------------------------------------------------------
??????? 511 recursive calls
???????? 81 db block gets
???? 180002 consistent gets
??????65131 physical reads
??????? 648 redo size
????? 17139 bytes sent via SQL*Net to client
???????1276 bytes received via SQL*Net from client
???????? 83 SQL*Net roundtrips to/from client
????????? 2 sorts (memory)
????????? 1 sorts (disk)
?????? 1229 rows processed
?
?
這個SQL執(zhí)行了2分半種以上,當(dāng)然這個SQL還可以進(jìn)行一下優(yōu)化,比如A的取值可以小于10000的開方,而B的取值可以小于10000除以最小的質(zhì)數(shù):
?
?
SQL> WITH T?
? 2?AS?
? 3?(SELECT ROWNUM RN FROM DUAL CONNECT BY LEVEL < 10000)
? 4?SELECT RN FROM T?
? 5?WHERE RN > 1
? 6?MINUS
? 7?SELECT A.RN * B.RN FROM T A, T B
? 8?WHERE A.RN <= B.RN
? 9?AND A.RN > 1
?10?AND A.RN <= 100
?11?AND B.RN > 1
?12?AND B.RN <= 5000;
?
1229 rows selected.
?
Elapsed: 00:00:00.78
?
Statistics
----------------------------------------------------------
????????? 2 recursive calls
???????? 23 db block gets
?????? 1820 consistent gets
???????? 16 physical reads
??????? 692 redo size
????? 17139 bytes sent via SQL*Net to client
?????? 1276 bytes received via SQL*Net from client
???????? 83 SQL*Net roundtrips to/from client
????????? 3 sorts (memory)
????????? 0 sorts (disk)
?????? 1229 rows processed
?
?
優(yōu)化后,SQL的執(zhí)行效率提高了3個數(shù)量級,其實(shí)這個SQL仍然是可以優(yōu)化的,考慮除了2以外,所有的質(zhì)數(shù)均為奇數(shù):
?
?
SQL> WITH T?
? 2?AS?
? 3?(SELECT ROWNUM * 2 + 1 RN FROM DUAL CONNECT BY LEVEL < 4999)
? 4?SELECT 2 FROM DUAL
? 5?UNION ALL
? 6?(
? 7?SELECT RN FROM T?
? 8?WHERE RN > 1
? 9?MINUS
?10?SELECT A.RN * B.RN FROM T A, T B
?11?WHERE A.RN <= B.RN
?12?AND A.RN > 1
?13?AND A.RN <= 100
?14?AND B.RN > 1
?15?AND B.RN <= 5000
?16?)
?17?;
?
1229 rows selected.
?
Elapsed: 00:00:00.25
?
Statistics
----------------------------------------------------------
????????? 2 recursive calls
???????? 15 db block gets
??????? 512 consistent gets
????????? 8 physical reads
??????? 648 redo size
????? 17138 bytes sent via SQL*Net to client
?????? 1276 bytes received via SQL*Net from client
???????? 83 SQL*Net roundtrips to/from client
????????? 3 sorts (memory)
????????? 0 sorts (disk)
?????? 1229 rows processed
?
雖然通過SQL優(yōu)化已經(jīng)將極大的提高了效率,但是和PLSQL比,效率仍然相去甚遠(yuǎn):
?
?
?
SQL> DECLARE
? 2?TYPE T_RECORD IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
? 3?V_RESULT T_RECORD;
? 4?I NUMBER DEFAULT 3;
? 5?N NUMBER DEFAULT 0;
? 6?BEGIN
? 7?--DBMS_OUTPUT.PUT_LINE(2);
? 8?V_RESULT(1) := 3;
? 9?WHILE(I < 10000) LOOP
?10?FOR J IN 1..V_RESULT.COUNT LOOP
?11?IF V_RESULT(J) * V_RESULT(J) > I THEN
?12?--DBMS_OUTPUT.PUT_LINE(I);
?13?V_RESULT(V_RESULT.COUNT + 1) := I;
?14?EXIT;
?15?END IF;
?16?IF TRUNC(I/V_RESULT(J)) = I/V_RESULT(J) THEN
?17?EXIT;
?18?END IF;
?19?END LOOP;
?20?IF N = 2 THEN
?21?I := I + 4;
?22?N := 1;
?23?ELSE
?24?I := I + 2;
?25?N := N + 1;
?26?END IF;
?27?END LOOP;
?28?V_RESULT(0) := 2;
?29?END;
?30?/
?
PL/SQL procedure successfully completed.
?
Elapsed: 00:00:00.04
?
這說明使用SQL并不一定就是最佳選擇,有的時候使用PLSQL效率反而會更高。使用合適的方法做適合的事情,一味追求使用單條SQL實(shí)現(xiàn)很可能會損失性能。
?
?
-The End-