27

我正在做一个为大学创建录取系统的项目;这些技术是 Java 和 Oracle。

在其中一张表中,存储了预先生成的序列号。稍后,根据这些序列号,将输入申请人的表格数据。我的要求是,当输入过程完成后,我必须生成一个 Lot wise 报告。如果在馈送预先生成的序列号期间,任何序列号都丢失了。

例如,在表格中,序列号为 7001、7002、7004、7005、7006、7010。从上述序列中可以清楚地看出,从 7001 到 7010,缺少的数字是 7003、7007、7008 和 7009

Oracle 中是否有任何可用的 DBMS 功能来找出这些数字,或者如果任何存储过程可以满足我的目的,那么请提出一个算法。

我可以在 Java 中找到一些技术,但为了速度,我想在 Oracle 中找到解决方案。

4

11 回答 11

51

无需对 9 进行硬编码的解决方案:

select min_a - 1 + level
     from ( select min(a) min_a
                 , max(a) max_a
              from test1
          )
  connect by level <= max_a - min_a + 1
    minus
   select a
     from test1

结果:

MIN_A-1+LEVEL
-------------
         7003
         7007
         7008
         7009

4 rows selected.
于 2012-06-10T13:29:52.620 回答
14

试试这个:

SELECT t1.SequenceNumber + 1 AS "From",
       MIN(t2.SequenceNumber) - 1 AS "To"
FROM MyTable t1
JOIN MyTable t2 ON t1.SequenceNumber < t2.SequenceNumber 
GROUP BY t1.SequenceNumber
HAVING t1.SequenceNumber + 1 < MIN(t2.SequenceNumber)

以下是序列 7001、7002、7004、7005、7006、7010 的结果:

From  To
7003  7003
7007  7009
于 2014-08-28T12:04:28.430 回答
2

这适用于 postgres >= 8.4。通过对 CTE 语法稍作修改,它也可以用于 oracle 和 microsoft。

-- EXPLAIN ANALYZE
WITH missing AS (
    WITH RECURSIVE fullhouse AS (
        SELECT MIN(num)+1 as num
        FROM numbers n0
        UNION ALL SELECT 1+ fh0.num AS num
        FROM fullhouse fh0
        WHERE EXISTS (
                SELECT * FROM numbers ex
                WHERE ex.num > fh0.num
                )
        )
        SELECT * FROM fullhouse fh1
        EXCEPT ( SELECT num FROM numbers nx)
        )
SELECT * FROM missing;
于 2012-06-10T14:35:09.347 回答
2

这是一个解决方案:

  • 依赖于 Oracle 的 LAG 功能
  • 不需要完整序列的知识(但因此不检测是否遗漏了序列中的第一个或最后一个数字)
  • 列出缺少的数字列表周围的值
  • 将缺失的数字列表列为连续组(可能便于报告)
  • 由于 listagg 限制,非常大的缺失数字列表不幸失败

SQL:

WITH MentionedValues /*this would just be your actual table, only defined here to provide data for this example */
        AS (SELECT *
              FROM (    SELECT LEVEL + 7000 seqnum
                          FROM DUAL
                    CONNECT BY LEVEL <= 10000)
             WHERE seqnum NOT IN (7003,7007,7008,7009)--omit those four per example
                                       ),
     Ranges /*identifies all ranges between adjacent rows*/
        AS (SELECT seqnum AS seqnum_curr,
                   LAG (seqnum, 1) OVER (ORDER BY seqnum) AS seqnum_prev,
                   seqnum - (LAG (seqnum, 1) OVER (ORDER BY seqnum)) AS diff
              FROM MentionedValues)
SELECT Ranges.*,
       (    SELECT LISTAGG (Ranges.seqnum_prev + LEVEL, ',') WITHIN GROUP (ORDER BY 1)
              FROM DUAL
        CONNECT BY LEVEL < Ranges.diff) "MissingValues" /*count from lower seqnum+1 up to lower_seqnum+(diff-1)*/
  FROM Ranges
 WHERE diff != 1 /*ignore when diff=1 because that means the numers are sequential without skipping any*/
;

输出:

SEQNUM_CURR SEQNUM_PREV DIFF MissingValues
7004        7002        2    "7003" 
7010        7006        4    "7007,7008,7009"                  
于 2017-11-14T16:50:24.753 回答
1

为您的方案获得答案的一种简单方法是:

create table test1 ( a number(9,0));

insert into test1 values (7001);
insert into test1 values (7002);
insert into test1 values (7004);
insert into test1 values (7005);
insert into test1 values (7006);
insert into test1 values (7010);
commit;

select n.n from (select ROWNUM + 7001 as n from dual connect by level <= 9) n 
   left join test1 t on n.n = t.a where t.a is null;

选择将为您提供示例中的答案。这只有在您事先知道您的数字在哪个范围内并且范围不应该太大时才有意义。第一个数字必须是ROWNUM零件中的偏移量,序列的长度是connect by零件中级别的限制。

于 2012-06-10T12:47:37.000 回答
1

我会connect by levelStefan 所做的那样建议,但是,您不能在此语句中使用子查询,这意味着它并不适合您,因为您需要知道序列的最大值和最小值是什么.

我建议使用管道表函数可能是生成连接所需的数字的最佳方式。为了使其工作,您需要数据库中的一个对象将值返回到:

create or replace type t_num_array as table of number;

然后是函数:

create or replace function generate_serial_nos return t_num_array pipelined is

   l_first number;
   l_last number;

begin

   select min(serial_no), max_serial_no)
     into l_first, l_last 
     from my_table
          ;

   for i in l_first .. l_last loop
      pipe row(i);
   end loop;

   return;

end generate_serial_nos;
/

使用此函数,以下将返回一个序列号列表,介于最小值和最大值之间。

select * from table(generate_serial_nos);

这意味着您查找缺少哪些序列号的查询变为:

select serial_no
  from ( select * 
           from table(generate_serial_nos) 
                ) generator 
  left outer join my_table actual
    on generator.column_value = actual.serial_no
 where actual.serial_no is null
于 2012-06-10T13:12:29.940 回答
1

这有效,但选择了第一个序列(起始值),因为它没有前任。在 SQL Server 中测试,但应该在 Oracle 中工作

SELECT
    s.sequence  FROM seqs s
WHERE
    s.sequence - (SELECT sequence FROM seqs WHERE sequence = s.sequence-1) IS NULL

这是一个测试结果

  Table
  -------------
  7000
  7001
  7004
  7005
  7007
  7008

  Result
  ----------
  7000
  7004
  7007

要获得未分配的序列,只需执行value[i] - 1i 大于第一行的位置,例如(7004 - 1 = 7003 and 7007 - 1 = 7006)哪些是可用序列

我认为您可以改进这个简单的查询

于 2012-06-10T13:39:59.480 回答
0
 SELECT ROWNUM "Missing_Numbers" FROM dual CONNECT BY LEVEL <= (SELECT MAX(a) FROM test1)
 MINUS
 SELECT a FROM test1 ;
于 2018-10-28T20:51:41.017 回答
0

改进的查询是:

SELECT ROWNUM "Missing_Numbers" FROM dual CONNECT BY LEVEL <= (SELECT MAX(a) FROM test1)
 MINUS
SELECT ROWNUM "Missing_Numbers" FROM dual CONNECT BY LEVEL < (SELECT Min(a) FROM test1)
 MINUS
SELECT a FROM test1;

注意:a 是我们发现缺失值的列。

于 2019-12-21T17:38:01.583 回答
0

尝试使用子查询:

SELECT A.EMPNO + 1 AS MissingEmpNo
FROM tblEmpMaster AS A
WHERE A.EMPNO + 1 NOT IN (SELECT EMPNO FROM tblEmpMaster)
于 2022-01-31T10:34:59.123 回答
-1
 select    A.ID + 1 As ID
From [Missing] As A
Where A.ID + 1 Not IN (Select ID from [Missing])
And A.ID < n

Data: ID
1
2
5
7
Result: ID
3
4
6
于 2016-08-23T07:40:54.903 回答