使用 SQL 查询打印素数

Posted

技术标签:

【中文标题】使用 SQL 查询打印素数【英文标题】:Print Prime Numbers with SQL query 【发布时间】:2016-09-11 02:52:42 【问题描述】:

我是 *** 的新手,遇到了一个打印 2 到 1000 的素数的查询。 如果这是最有效的编码方式,我使用了以下查询需要输入。

WITH NUM AS (
    SELECT LEVEL N 
    FROM DUAL CONNECT BY LEVEL <= 1000
) 
SELECT LISTAGG(B.N,'-') WITHIN GROUP(ORDER BY B.N) AS PRIMES 
FROM (
    SELECT  N,
            CASE WHEN EXISTS (
                                SELECT NULL 
                                FROM NUM N_INNER 
                                WHERE N_INNER .N > 1 
                                AND N_INNER.N < NUM.N 
                                AND MOD(NUM.N, N_INNER.N)=0
                            ) THEN 
                'NO PRIME' 
            ELSE 
                'PRIME' 
            END IS_PRIME 
        FROM NUM
    ) B 
WHERE B.IS_PRIME='PRIME' 
AND B.N!=1;

我知道这个问题已经被问过多次,如果有的话,我请求更好的解决方案。更多关于它如何与 mysql/MS SQL/PostgreSQL 一起工作的需要输入。

任何帮助都会让我更好地理解。

【问题讨论】:

您能详细说明您想要实现的目标吗?您提出的查询是Oracle 特定的。您是否需要其他 RDBMS 中的等价物或更好的算法来获得素数? 是的,如果可以的话,我需要更好的算法。我能够获得在其他平台上编写查询的详细信息。 【参考方案1】:
SELECT  GROUP_CONCAT(distinct PRIME_NUMBER SEPARATOR '&')
FROM (SELECT @prime:=@prime + 1 AS PRIME_NUMBER
    FROM information_schema.tables
    CROSS JOIN (SELECT @prime:=1) r
    WHERE @num <1000
    ) p
WHERE NOT EXISTS (
SELECT * FROM
    (SELECT @divisor := @divisor + 1 AS DIVISOR FROM
    information_schema.tables 
    CROSS JOIN (SELECT @divisor:=1) r1
     WHERE @divisor <=1000
    ) d
WHERE MOD(PRIME_NUMBER, DIVISOR) = 0 AND PRIME_NUMBER != DIVISOR) ;
    enter code here

解释:

    两个内部 SELECT(SELECT @prime 和 SELECT @divisor)创建两个列表。它们都包含从 1 到 1000 的数字。第一个列表是“潜在素数列表”,第二个是“除数列表”

    然后,我们遍历潜在素数列表(外部 SELECT),并为该列表中的每个数字寻找除数(SELECT * FROM 子句),该除数可以在没有提示的情况下除数并且不等于数字(WHERE MOD... 子句)。如果至少存在一个这样的除数,则该数字不是素数且未被选中(WHERE NOT EXISTS... 子句)。

【讨论】:

【参考方案2】:

曾在甲骨文工作:

SELECT LISTAGG(a,'&')
     WITHIN GROUP (ORDER BY a) 
FROM(WITH x AS(
    SELECT level+1 x
    FROM dual
    CONNECT BY LEVEL <= 999
)

SELECT x.x as a
FROM x
WHERE NOT EXISTS (
  SELECT 1 FROM x y
  WHERE x.x > y.x AND remainder( x.x, y.x) = 0
));

【讨论】:

【参考方案3】:

这在 MySql 中对我有用:

select '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&101&103&107&109&113&127&131&137&139&149&151&157&163&167&173&179&181&191&193&197&199&211&223&227&229&233&239&241&251&257&263&269&271&277&281&283&293&307&311&313&317&331&337&347&349&353&359&367&373&379&383&389&397&401&409&419&421&431&433&439&443&449&457&461&463&467&479&487&491&499&503&509&521&523&541&547&557&563&569&571&577&587&593&599&601&607&613&617&619&631&641&643&647&653&659&661&673&677&683&691&701&709&719&727&733&739&743&751&757&761&769&773&787&797&809&811&821&823&827&829&839&853&857&859&863&877&881&883&887&907&911&919&929&937&941&947&953&967&971&977&983&991&997';

好吧,我知道上面的代码只是硬编码的,你可以运行这个问题,但这不是我们作为程序员应该追求的,所以这是我的 oracle 解决方案:

SELECT LISTAGG(L1,'&') WITHIN GROUP (ORDER BY L1) FROM (Select L1 FROM (SELECT LEVEL L1 FROM DUAL CONNECT BY LEVEL<=1000) Where L1 <> 1 MINUS select L1 from (SELECT LEVEL L1 FROM DUAL CONNECT BY LEVEL<=1000) A , (SELECT LEVEL L2 FROM DUAL CONNECT BY LEVEL<=1000) B Where L2<=L1 and MOD(L1,L2)=0 AND L1<>L2 AND L2<>1);

【讨论】:

这并不能完全解决问题,因为 OP 希望使用 SQL 代码通过计算而不是通过在打印语句中进行硬编码来创建它。【参考方案4】:

适用于 MySQL 8 或更高版本

/* create a table with one row and that starts with  2 ends at 1000*/

SET cte_max_recursion_depth = 1001; /* works for MySQL 8.0*/
;WITH RECURSIVE sequence AS (
    SELECT 1 AS l
    UNION ALL
    SELECT l + 1 AS value
    FROM sequence
    WHERE sequence.l < 1000
),

/* create a caretesian product of a number to other numbers uptil this very number
so for example if there is a value 5 in a row then it creates these rows using the table below
(5,2), (5,3), (5,4), (5,5) */

J as (
SELECT (a.l) as m  , (b.l) as n 
FROM sequence a, sequence b
WHERE b.l <= a.l)
,

/*take a row from column 1 then divide it with other column values but group by column 1 first,
note the completely divisible count*/
f as 
( SELECT m , SUM(CASE WHEN mod(m,n) = 0 THEN 1  END) as fact
FROM  J 
GROUP BY m
HAVING fact = 2
ORDER BY m ASC /*this view return numbers in descending order so had to use order by*/
)

/* this is for string formatting, converting a column to a string with separator &*/
SELECT group_concat(m SEPARATOR '&') FROM f;

【讨论】:

【参考方案5】:

--创建表 prime_number_t 创建表 prime_number_t ( integervalue_c 整数非空主键 );

--向表中插入数据 prime_number_t 全部插入 转化为 prime_number_t(integervalue_c) 值 (1) 转化为 prime_number_t(integervalue_c) 值 (2) 转化为 prime_number_t(integervalue_c) 值 (3) 转化为 prime_number_t(integervalue_c) 值 (4) 转化为 prime_number_t(integervalue_c) 值 (5) 转化为 prime_number_t(integervalue_c) 值 (6) 转化为 prime_number_t(integervalue_c) 值 (7) 转化为 prime_number_t(integervalue_c) 值 (8) 转化为 prime_number_t(integervalue_c) 值 (9) 转化为 prime_number_t(integervalue_c) 值 (10) 从双重中选择 1;

提交;

--编写SQL语句判断下列数字中哪些是素数 --same 查询也适用于 REMAINDER 函数,而不是 MOD 函数 与 cte_prime_number_t 作为 ( 选择整数值_c 从 prime_number_t 按 integervalue_c 排序 ), cte_maxval AS ( select max(integervalue_c) AS maxval FROM cte_prime_number_t ), cte_level AS ( 选择 LEVEL+1 作为 lvl 从双, cte_maxval 按级别连接 cpn.lvl AND mod(cpnt.integervalue_c,cpn.lvl) = 0 ) 按 PrimeNumbers 排序;

【讨论】:

【参考方案6】:
SELECT LISTAGG(PRIME_NUMBER,'&') WITHIN GROUP (ORDER BY PRIME_NUMBER) 
FROM
(
    SELECT L PRIME_NUMBER FROM
    (
        SELECT LEVEL L FROM DUAL CONNECT BY LEVEL <= 1000 ), 
        (
            SELECT LEVEL M FROM DUAL CONNECT BY LEVEL <= 1000
        ) WHERE M <= L 
        GROUP BY L 
        HAVING COUNT(CASE WHEN L/M = TRUNC(L/M) THEN 'Y' END
    ) = 2 
    ORDER BY L
);

【讨论】:

【参考方案7】:
/* Below is my solution */

/* Step 1: Get all the numbers till 1000 */
with tempa as
(
  select level as Num
  from dual
  connect by level<=1000
),

/* Step 2: Get the Numbers for finding out the factors */

tempb as
(
    select a.NUm,b.Num as Num_1
    from tempa a , tempa b
    where b.Num<=a.Num
),

/*Step 3:If a number has exactly 2 factors, then it is a prime number */

tempc as
(
    select Num, sum(case when mod(num,num_1)=0 then 1 end) as Factor_COunt
    from tempb
    group by Num
)
select listagg(Num,'&') within group (order by Num)
from tempc
where Factor_COunt=2
;

【讨论】:

【参考方案8】:

这就是在 SQL 服务器中对我有用的方法。我试图减少嵌套循环的顺序。

declare @var int
declare @i int
declare @result varchar (max)
set @var = 1
select @result = '2&3&5' --first few obvious prime numbers
while @var < 1000  --the first loop
begin
set @i = 3;
while @i <= @var/2  --the second loop which I attempted to reduce the order
begin
if @var%@i = 0
break;
if @i=@var/2 
begin
set @result = @result + '&' + CAST(@var AS VARCHAR)
break;
end
else 
set @i = @i + 1 
end
set @var = @var + 1;
end
print @result

【讨论】:

【参考方案9】:

MySQL 查询解决方案

我已经在mysql中解决了这个问题,如下:

SET @range = 1000;

SELECT GROUP_CONCAT(R2.n SEPARATOR '&')
FROM (
        SELECT @ctr2:=@ctr2+1 "n"
        FROM information_schema.tables R2IS1,
        information_schema.tables R2IS2,
        (SELECT @ctr2:=1) TI
        WHERE @ctr2<@range
     ) R2
WHERE NOT EXISTS (
                SELECT R1.n
                FROM (
                    SELECT @ctr1:=@ctr1+1 "n"
                    FROM information_schema.tables R1IS1,
                    information_schema.tables R1IS2,
                    (SELECT @ctr1:=1) I1
                    WHERE @ctr1<@range
                ) R1
                WHERE R2.n%R1.n=0 AND R2.n>R1.n
        )

注意: information_schema.tables 的数量应增加以扩大范围,例如如果范围为 100000,请自行检查设置信息表。

【讨论】:

【参考方案10】:

SQL Server 的最简单方法

DECLARE @range int = 1000, @x INT = 2, @y INT = 2 

While (@y <= @range)
BEGIN
 while (@x <= @y) 
 begin
    IF ((@y%@x) =0) 
    BEGIN
        IF (@x = @y) 
            PRINT @y
            break
    END
 IF ((@y%@x)<>0)   
 set @x = @x+1
 end  
set @x = 2
set @y = @y+1 
end

【讨论】:

【参考方案11】:

一个简单的也可以这样

select level id1 from dual connect by level < 2001
minus
select distinct id1 from (select level id1 from dual connect by level < 46) t1 inner join (select level id2 from dual connect by level < 11) t2
on 1=1 where t1.id1> t2.id2 and mod(id1,id2)=0 and id2<>1

【讨论】:

【参考方案12】:

Oracle 并且在获取部分时没有内部选择:

 with tmp(id)
as (
    select level  id from dual connect by level <= 100 
) select t1.id from tmp t1
 JOIN tmp t2
 on MOD(t1.id, t2.id) = 0
 group by t1.ID
 having count(t1.id) = 2
 order by t1.ID
 ;

【讨论】:

【参考方案13】:
For SQL Server We can use below CTE 

SET NOCOUNT ON

;WITH Prim AS
(
    SELECT 2 AS Value 
    UNION ALL
    SELECT t.Value+1 AS VAlue 
    FROM Prim t
    WHERE t.Value < 1000
)SELECT * 
FROM Prim t
WHERE NOT EXISTS(   SELECT 1 FROM prim t2
                WHERE t.Value % t2.Value = 0 
                AND t.Value != t2. Value)
OPTION (MAXRECURSION 0)

【讨论】:

请使用正确的代码格式以使您的答案可读【参考方案14】:

在 sqlite3 上测试

WITH nums(n) AS 
(
    SELECT 1
    UNION ALL
    SELECT n + 1 FROM nums WHERE n < 100
)

SELECT n 
FROM (
  SELECT n FROM nums
) 
WHERE n NOT IN (
  SELECT n
  FROM nums 
  JOIN ( SELECT n AS n2 FROM nums )
  WHERE n  <> 1 
    AND n2 <> 1 
    AND n  <> n2 
    AND n2 <  n 
    AND n % n2 = 0
  ORDER BY n
)
AND n <> 1

在 Vertica 8 上测试

WITH seq AS (
  SELECT ROW_NUMBER() OVER() AS n 
  FROM (
      SELECT 1 
      FROM (
          SELECT date(0) + INTERVAL '1 second' AS i 
          UNION ALL
          SELECT date(0) + INTERVAL '100 seconds' AS i 
      ) _
      TIMESERIES tm AS '1 second' OVER(ORDER BY i)
  ) _
)
SELECT n 
FROM (SELECT n FROM seq) _  
WHERE n NOT IN (
  SELECT n FROM (
    SELECT s1.n AS n, s2.n AS n2
    FROM seq AS s1 
    CROSS JOIN seq AS s2
    ORDER BY n, n2
  ) _
  WHERE n  <> 1 
    AND n2 <> 1 
    AND n  <> n2 
    AND n2 <  n 
    AND n % n2 = 0
)
AND n <> 1
ORDER BY n

【讨论】:

【参考方案15】:

PostgreSQL 中的简单查询:

SELECT serA.el AS prime
FROM generate_series(2, 100) serA(el)
LEFT JOIN generate_series(2, 100) serB(el) ON serA.el >= POWER(serB.el, 2)
                                              AND serA.el % serB.el = 0
WHERE serB.el IS NULL

享受吧! :)

【讨论】:

【参考方案16】:

以下代码用于在 SQL 中查找素数

在本地服务器的 SampleDB 上测试

CREATE procedure sp_PrimeNumber(@number int)
as 
begin
declare @i int
declare @j int
declare @isPrime int
set @isPrime=1
set @i=2
set @j=2
while(@i<=@number)
begin
    while(@j<=@number)
    begin
        if((@i<>@j) and (@i%@j=0))
        begin
            set @isPrime=0
            break
        end
        else
        begin
            set @j=@j+1
        end
    end
    if(@isPrime=1)
    begin
        SELECT @i
    end
    set @isPrime=1
    set @i=@i+1
    set @j=2
end
end

我已经创建了一个存储过程,它有一个参数@number 来查找直到给定数字的素数

为了得到质数我们可以执行下面的存储过程

EXECUTE sp_PrimeNumber 100  -- gives prime numbers up to 100

如果您不熟悉存储过程并想在 SQL 中查找素数,我们可以使用以下代码

在主数据库上测试

declare @i int
declare @j int
declare @isPrime int
set @isPrime=1
set @i=2
set @j=2
while(@i<=100)
begin
    while(@j<=100)
    begin
        if((@i<>@j) and (@i%@j=0))
        begin
            set @isPrime=0
            break
        end
        else
        begin
            set @j=@j+1
        end
    end
    if(@isPrime=1)
    begin
        SELECT @i
    end
    set @isPrime=1
    set @i=@i+1
    set @j=2
end

这段代码可以给出 1 到 100 之间的素数。如果我们想找到更多的素数,请在 while 循环中编辑 @i 和 @j 参数并执行

【讨论】:

在 SSMS 中我们可以执行存储过程,然后使用所需的参数执行它以获得所需的结果【参考方案17】:

MySQL 代码:

DECLARE 
@i INT, 
@a INT, 
@count INT, 
@p nvarchar(max)
SET @i = 1 
WHILE (@i <= 1000) 
BEGIN SET @count = 0 
SET @a = 1 
WHILE (@a <= @i) 
BEGIN IF (@i % @a = 0) SET @count = @count + 1 SET @a = @a + 1 
END IF (@count = 2) SET @P = CONCAT(@P,CONCAT(@i,'&')) SET @i = @i + 1 
END
PRINT LEFT(@P, LEN(@P) - 1)

【讨论】:

OP 正在寻找一种比他们目前在他们的问题中拥有的代码更有效的解决方案。你能解释一下你的代码到底在做什么以及它的改进来自哪里吗?【参考方案18】:

MariaDB(带有序列插件)

类似于 kordirkos 算法:

select 2 as p union all
select n.seq
from seq_3_to_1000_step_2 n
where not exists (
    select 1
    from seq_3_to_32_step_2 q
    where q.seq < n.seq
      and n.seq mod q.seq = 0
);

使用左连接:

select 2 as p union all
select n.seq
from seq_3_to_1000_step_2 n
left join seq_3_to_32_step_2 q
      on  q.seq < n.seq
      and n.seq mod q.seq = 0
where q.seq is null;

MySQL

MySQL 中没有序列生成助手。所以必须先创建序列表:

drop temporary table if exists n;
create temporary table if not exists n engine=memory
    select t2.c*100 + t1.c*10 + t0.c + 1 as seq from 
    (select 0 c union all select 1 c union all select 2 c union all select 3 c union all select 4 c union all select 5 c union all select 6 c union all select 7 c union all select 8 c union all select 9 c) t0,
    (select 0 c union all select 1 c union all select 2 c union all select 3 c union all select 4 c union all select 5 c union all select 6 c union all select 7 c union all select 8 c union all select 9 c) t1,
    (select 0 c union all select 1 c union all select 2 c union all select 3 c union all select 4 c union all select 5 c union all select 6 c union all select 7 c union all select 8 c union all select 9 c) t2
    having seq > 2 and seq % 2 != 0;

drop temporary table if exists q;
create temporary table if not exists q engine=memory
    select *
    from n
    where seq <= 32;
alter table q add primary key seq (seq);

现在可以使用类似的查询:

select 2 as p union all
select n.seq
from n
where not exists (
    select 1
    from q
    where q.seq < n.seq
      and n.seq mod q.seq = 0
);

select 2 as p union all
select n.seq
from n
left join q
    on  q.seq < n.seq
    and n.seq mod q.seq = 0
where q.seq is null;

sqlfiddle

【讨论】:

【参考方案19】:

在 PostgreSQL 中,打印最多 1000 个素数的最快查询可能是:

SELECT regexp_split_to_table('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,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997',E',')::int
AS x
;

在我的电脑上只用了 16 毫秒。

注意:素数列表从https://en.wikipedia.org/wiki/Prime_number复制而来 并粘贴到这个长字符串中

如果您更喜欢 SQL,那么这可行

WITH x AS (
  SELECT * FROM generate_series( 2, 1000 ) x
)
SELECT x.x
FROM x
WHERE NOT EXISTS (
  SELECT 1 FROM x y
  WHERE x.x > y.x AND x.x % y.x = 0
)
;

它慢了两倍 - 31 毫秒。


Ans 是 Oracle 的等效版本:

WITH x AS(
    SELECT level+1 x
    FROM dual
    CONNECT BY LEVEL <= 999
)
SELECT x.x
FROM x
WHERE NOT EXISTS (
  SELECT 1 FROM x y
  WHERE x.x > y.x AND remainder( x.x, y.x) = 0
)
;

【讨论】:

您可以仅使用 2 和赔率将时间减半:SELECT 2 x UNION ALL SELECT * FROM generate_series(3, 1000, 2) x【参考方案20】:

最明显的改进是不再检查从 1 到 n,而是从 1 到 n 的平方根。

第二个主要优化是使用临时表来存储结果并首先检查它们。这样你可以从 1 到 n 递增迭代,并且只检查从 1 到 n 的平方根的已知素数(递归地这样做,直到你有一个列表)。如果您以这种方式处理事情,您可能希望在函数中设置素数检测,然后对您的数列生成器执行相同操作。

第二个虽然意味着扩展 SQL,所以我不知道这是否符合您的要求。

对于 postgresql,我将使用 generate_series 生成数字列表。然后,我将创建函数,然后将素数列表存储在临时表中,或者将它们传入和传出有序数组,然后像这样耦合它们

【讨论】:

以上是关于使用 SQL 查询打印素数的主要内容,如果未能解决你的问题,请参考以下文章

素数打印机停止在 251,为啥? [复制]

打印100~200之间的素数

java 打印素数

Java打印素数(质数)

在python中打印一系列素数

Python 打印素数