web-dev-qa-db-fra.com

vérifier si "c'est un nombre" fonction dans Oracle

J'essaie de vérifier si une valeur d'une colonne dans une requête Oracle (10g) est un nombre afin de la comparer. Quelque chose comme:

select case when ( is_number(myTable.id) and (myTable.id >0) ) 
            then 'Is a number greater than 0' 
            else 'it is not a number' 
       end as valuetype  
  from table myTable

Des idées sur la façon de vérifier cela?

35
Fgblanch

En supposant que la colonne ID dans myTable ne soit pas déclarée comme NUMBER (ce qui semble être un choix étrange et susceptible de poser problème), vous pouvez écrire une fonction qui tente de convertir l'ID (probablement VARCHAR2) en un nombre , intercepte l’exception et renvoie un "Y" ou un "N". Quelque chose comme

CREATE OR REPLACE FUNCTION is_number( p_str IN VARCHAR2 )
  RETURN VARCHAR2 DETERMINISTIC PARALLEL_ENABLE
IS
  l_num NUMBER;
BEGIN
  l_num := to_number( p_str );
  RETURN 'Y';
EXCEPTION
  WHEN value_error THEN
    RETURN 'N';
END is_number;

Vous pouvez ensuite intégrer cet appel à une requête, c'est-à-dire.

SELECT (CASE WHEN is_number( myTable.id ) = 'Y' AND myTable.id > 0 
               THEN 'Number > 0'
             ELSE 'Something else'
         END) some_alias
  FROM myTable

Notez que bien que PL/SQL ait un type de données booléen, SQL n'en a pas. Ainsi, bien que vous puissiez déclarer une fonction qui retourne un booléen, vous ne pouvez pas utiliser une telle fonction dans une requête SQL.

32
Justin Cave

Une autre idée mentionnée ici consiste à utiliser une expression régulière pour vérifier:

SELECT  foo 
FROM    bar
WHERE   REGEXP_LIKE (foo,'^[[:digit:]]+$');

La partie intéressante est que vous n’avez pas besoin d’une fonction PL/SQL distincte. La partie potentiellement problématique est qu'une expression régulière peut ne pas être la méthode la plus efficace pour un grand nombre de lignes.

61
Saish

La réponse de Saish en utilisant REGEXP_LIKE est la bonne idée mais ne supporte pas les nombres flottants. Celui-ci sera ...

Renvoie des valeurs numériques

SELECT  foo 
FROM    bar
WHERE   REGEXP_LIKE (foo,'^-?\d+(\.\d+)?$');

Renvoie des valeurs non numériques

SELECT  foo 
FROM    bar
WHERE   NOT REGEXP_LIKE (foo,'^-?\d+(\.\d+)?$');

Vous pouvez tester vos expressions régulières elles-mêmes jusqu'à ce que votre cœur soit content à http://regexpal.com/ (mais assurez-vous de cocher la case correspondante aux sauts de ligne pour celui-ci).

15
Matt Byrne

Il s'agit d'un doublon potentiel de Recherche de lignes qui ne contiennent pas de données numériques dans Oracle . Voir aussi: Comment puis-je déterminer si une chaîne est numérique en SQL? .

Voici une solution basée sur Michael Durrant's qui fonctionne pour les entiers.

SELECT foo
FROM bar
WHERE DECODE(TRIM(TRANSLATE(your_number,'0123456789',' ')), NULL, 'number','contains char') = 'number'

Adrian Carneiro a publié une solution qui fonctionne pour les nombres décimaux et autres. Cependant, comme l'a souligné Justin Cave, cela classera incorrectement des chaînes telles que '123.45.23.234' ou '131 + 234'.

SELECT foo
FROM bar
WHERE DECODE(TRIM(TRANSLATE(your_number,'+-.0123456789',' ')), NULL, 'number','contains char') = 'number'

Si vous avez besoin d’une solution sans PL/SQL ou REGEXP_LIKE, cela peut aider.

5
kevlened

Vous pouvez utiliser la fonction d'expression régulière 'regexp_like' dans Oracle (10g) comme ci-dessous:

select case
       when regexp_like(myTable.id, '[[:digit:]]') then
        case
       when myTable.id > 0 then
        'Is a number greater than 0'
       else
        'Is a number less than or equal to 0'
     end else 'it is not a number' end as valuetype
from table myTable
5
Kanu

Je suis contre utiliser when others donc je l’utiliserais (retourne un "entier booléen" car SQL ne supporte pas les booléens)

create or replace function is_number(param in varchar2) return integer
 is
   ret number;
 begin
    ret := to_number(param);
    return 1; --true
 exception
    when invalid_number then return 0;
 end;

Dans l'appel SQL, vous utiliseriez quelque chose comme:

select case when ( is_number(myTable.id)=1 and (myTable.id >'0') ) 
            then 'Is a number greater than 0' 
            else 'it is not a number or is not greater than 0' 
       end as valuetype  
  from table myTable
2
Filipe Silva
CREATE OR REPLACE FUNCTION is_number(N IN VARCHAR2) RETURN NUMBER IS
  BEGIN
    RETURN CASE regexp_like(N,'^[\+\-]?[0-9]*\.?[0-9]+$') WHEN TRUE THEN 1 ELSE 0 END;
END is_number;

Veuillez noter que 45e4 ne sera pas considéré comme un nombre, mais vous pouvez toujours changer de regex pour accomplir le contraire.

1
Waqas Ashraf

Voici ma requête pour trouver tous ceux qui ne sont PAS numérotés:

Select myVarcharField
From myTable
where not REGEXP_LIKE(myVarcharField, '^(-)?\d+(\.\d+)?$', '')
and not REGEXP_LIKE(myVarcharField, '^(-)?\d+(\,\d+)?$', '');

Dans mon domaine j'ai. et, malheureusement, les nombres décimaux ont dû en tenir compte, sinon vous n’avez besoin que de l’une des restrictions.

1
TheBakker

Comment est définie la colonne? Si c'est un champ varchar, alors ce n'est pas un nombre (ou stocké en tant que tel). Oracle pourra peut-être effectuer la conversion pour vous (par exemple, sélectionnez * depuis une table où charField = 0), mais il ne renverra que les lignes dans lesquelles la conversion est vraie et possible. C’est aussi loin de la situation idéale en termes de performances.

Donc, si vous voulez faire des comparaisons de nombres et traiter cette colonne comme un nombre, peut-être devrait-elle être définie comme un nombre?

Cela dit, voici ce que vous pourriez faire:

create or replace function myToNumber(i_val in varchar2) return number is
 v_num number;
begin
 begin
   select to_number(i_val) into v_num from dual;
 exception
   when invalid_number then
   return null;
 end;
 return v_num;
end;

Vous pouvez également inclure les autres paramètres du numéro to_number habituel. Utilisez comme tel:

select * from someTable where myToNumber(someCharField) > 0;

Il ne renverra aucune ligne considérée par Oracle comme un nombre non valide.

À votre santé.

1
tbone

@JustinCave - Le remplacement de "when" par "quand les autres" est un raffinement de votre approche ci-dessus. Ce léger tweak supplémentaire, bien que conceptuellement identique, supprime la nécessité de définir et d’allouer de la mémoire à votre variable l_num:

function validNumber(vSomeValue IN varchar2)
     return varchar2 DETERMINISTIC PARALLEL_ENABLE
     is
begin
  return case when abs(vSomeValue) >= 0 then 'T' end;
exception
  when value_error then
    return 'F';
end;

Juste une note également pour tous ceux qui préfèrent émuler la logique de format de nombre Oracle en utilisant l’approche REGEXP "plus risquée", n’oubliez pas de prendre en compte NLS_NUMERIC_CHARACTERS et NLS_TERRITORY.

1
Pancho

eh bien, vous pouvez créer la fonction is_number à appeler pour que votre code fonctionne.

create or replace function is_number(param varchar2) return boolean
 as
   ret number;
 begin
    ret := to_number(param);
    return true;
 exception
    when others then return false;
 end;

EDIT: S'il vous plaît reporter à la réponse de Justin. J'ai oublié ce petit détail pour un appel SQL pur ....

0
Michael Broughton

Vous pouvez utiliser cet exemple

SELECT NVL((SELECT 1 FROM  DUAL WHERE   REGEXP_LIKE (:VALOR,'^[[:digit:]]+$')),0) FROM DUAL;
0
Omar Medrano