27

私は大学の入学システムを作成するプロジェクトを行っています。テクノロジーは Java と Oracle です。

テーブルの 1 つには、事前に生成されたシリアル番号が格納されています。後で、それらのシリアル番号に対して、申請者のフォーム データが入力されます。私の要件は、エントリープロセスが完了したら、ロットごとのレポートを生成する必要があるということです. 事前に生成されたシリアル番号のフィード中に、シーケンス番号が失われた場合。

たとえば、テーブルでは、シーケンス番号が 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 に答える
2

これはpostgres>=8.4で機能します。CTE構文に若干の変更を加えることで、オラクルとマイクロソフトでも機能するようにすることができます。

-- 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 関数に依存
  • 完全なシーケンスの知識は必要ありません (ただし、シーケンスの最初または最後の番号が欠落しているかどうかは検出されません)。
  • 欠落している数値のリストを囲む値をリストします
  • 不足している番号のリストを連続したグループとして一覧表示します (おそらくレポートに便利です)
  • listag の制限により、欠落している数の非常に大きなリストでは悲劇的に失敗します

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

これは機能しましたが、前任者がないため、最初のシーケンス (開始値) が選択されます。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] - 1は、最初の行で i が大きい場合に実行します。たとえば(7004 - 1 = 7003 and 7007 - 1 = 7006)、使用可能なシーケンスはどれですか

この単純なクエリを改善できると思います

于 2012-06-10T13:39:59.480 に答える
1

シナリオの答えを得る簡単な方法の 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 levelしましたが、このステートメントではサブクエリを使用できません。つまり、シーケンスの最大値と最小値を知る必要があるため、実際には適していません。 .

結合を行うために必要な数値を生成するには、パイプラインのテーブル関数が最適な方法である可能性があることをお勧めします。これが機能するためには、データベース内に値を返すオブジェクトが必要です。

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 に答える
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 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 に答える