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

我是 StackOverflow 的新手,遇到了一个打印 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 中的等价物或更好的算法来获得素数?
  • 是的,如果可以的话,我需要更好的算法。我能够获得在其他平台上编写查询的详细信息。

标签: mysql sql-server postgresql select subquery


【解决方案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

解释:

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

  2. 然后,我们遍历潜在素数列表(外部 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 毫秒。


                              如果您更喜欢 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 生成数字列表。然后,我将创建函数,然后将素数列表存储在临时表中,或者将它们传入和传出有序数组,然后像这样耦合它们

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2019-07-22
                                • 2021-01-13
                                • 1970-01-01
                                • 2011-01-20
                                • 2012-03-15
                                • 1970-01-01
                                相关资源
                                最近更新 更多