Je suis en train de créer un système d'admission pour un collège. les technologies sont Java et Oracle.
Dans l'une des tables, les numéros de série pré-générés sont stockés. Par la suite, en fonction de ces numéros de série, les données de formulaire du demandeur seront entrées. Mon exigence est que lorsque le processus d’inscription soit terminé, je devrai générer un rapport détaillé. Si lors de l’alimentation des numéros de série pré-générés, des numéros de séquence ont disparu.
Par exemple, disons dans un tableau, les numéros de séquence sont 7001, 7002, 7004, 7005, 7006, 7010 . Dans la série ci-dessus, il est clair que de 7001 à 7010 les numéros manquants sont 7003, 7007, 7008 et 7009
Existe-t-il une fonction SGBD disponible dans Oracle pour connaître ces numéros ou si une procédure stockée peut remplir mon objectif, veuillez suggérer un algorithme.
Je peux trouver des techniques en Java, mais pour la rapidité, je veux trouver la solution dans Oracle.
Une solution sans coder en dur le 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
Résultats:
MIN_A-1+LEVEL
-------------
7003
7007
7008
7009
4 rows selected.
Essaye ça:
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)
Voici le résultat pour la séquence 7001, 7002, 7004, 7005, 7006, 7010:
From To
7003 7003
7007 7009
Cela fonctionne sur postgres> = 8.4. Avec quelques légères modifications de la syntaxe CTE, il pourrait également être adapté à Oracle et 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;
Cela a fonctionné mais sélectionne la première séquence (valeur de départ) car il n’a pas de prédécesseur. Testé sous SQL Server mais devrait fonctionner sous Oracle
SELECT
s.sequence FROM seqs s
WHERE
s.sequence - (SELECT sequence FROM seqs WHERE sequence = s.sequence-1) IS NULL
Voici un résultat de test
Table
-------------
7000
7001
7004
7005
7007
7008
Result
----------
7000
7004
7007
Pour obtenir une séquence non assignée, il suffit de faire value[i] - 1
où i est supérieur à la première ligne, par exemple. (7004 - 1 = 7003 and 7007 - 1 = 7006)
qui sont des séquences disponibles
Je pense que vous pouvez améliorer cette requête simple
J'aurais suggéré connect by level
comme Stefan l'a fait , cependant, vous ne pouvez pas utiliser de sous-requête dans cette instruction, ce qui signifie que cela ne vous convient pas vraiment, car vous devez savoir ce que le maximum et le minimum les valeurs de votre séquence sont.
Je suggérerais une fonction de tableau bordée de tubes pourrait être le meilleur moyen de générer les nombres dont vous avez besoin pour effectuer la jointure. Pour que cela fonctionne, vous avez besoin d'un objet dans votre base de données pour renvoyer les valeurs à:
create or replace type t_num_array as table of number;
Puis la fonction:
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;
/
En utilisant cette fonction, ce qui suit renverrait une liste de numéros de série, entre le minimum et le maximum.
select * from table(generate_serial_nos);
Ce qui signifie que votre requête pour savoir quels numéros de série sont manquants devient:
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
Voici une solution qui:
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*/
;
Sortie:
SEQNUM_CURR SEQNUM_PREV DIFF MissingValues
7004 7002 2 "7003"
7010 7006 4 "7007,7008,7009"
Voici un moyen simple d'obtenir votre réponse à votre scénario:
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;
La sélection vous donnera la réponse de votre exemple. Cela n'a de sens que si vous savez à l'avance dans quelle fourchette se trouvent vos chiffres et que la fourchette ne devrait pas être trop grande. Le premier nombre doit être le décalage dans la partie ROWNUM
et la longueur de la séquence est la limite du niveau dans la partie connect by
.
SELECT ROWNUM "Missing_Numbers" FROM dual CONNECT BY LEVEL <= (SELECT MAX(a) FROM test1)
MINUS
SELECT a FROM test1 ;