私は大学の入学システムを作成するプロジェクトを行っています。テクノロジーはJavaおよびOracleです。
いずれかのテーブルには、事前に生成されたシリアル番号が保存されています。後で、それらのシリアル番号に対して、申請者のフォームデータが入力されます。私の要件は、エントリプロセスが完了したら、ロットごとのレポートを生成する必要があることです。事前に生成されたシリアル番号の供給中に、シーケンス番号が欠落した場合。
たとえば、表では、シーケンス番号は7001、7002、7004、7005、7006、7010です。上記のシリーズから、7001から7010までの番号が7003、7007、7008、7009であることは明らかです。
これらの数値を見つけるためにOracleで使用できるDBMS関数はありますか、またはストアドプロシージャが私の目的を達成できる場合は、アルゴリズムを提案してください。
Javaでいくつかのテクニックを見つけることができますが、速度のためにOracleで解決策を見つけたいです。
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.
これを試して:
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
これは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;
これは機能しましたが、先行タスクがないため、最初のシーケンス(開始値)を選択します。 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)
使用可能なシーケンス
この単純なクエリを改善できると思います
connect by level
as Stefanが行った ただし、このステートメントでサブクエリを使用することはできません。つまり、最大値と最小値を知る必要があるため、実際には適切ではありません。シーケンスの値は次のとおりです。
パイプラインテーブル関数 を使用することをお勧めします。これは、結合に必要な数字を生成するための最良の方法です。これが機能するためには、データベースに値を返すオブジェクトが必要です:
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
以下がその解決策です。
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"
シナリオの答えを得る簡単な方法の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
部分のレベルの制限です。
SELECT ROWNUM "Missing_Numbers" FROM dual CONNECT BY LEVEL <= (SELECT MAX(a) FROM test1)
MINUS
SELECT a FROM test1 ;