web-dev-qa-db-fra.com

Recherche de lignes qui ne contiennent pas de données numériques dans Oracle

J'essaie de localiser des enregistrements problématiques dans une très grande table Oracle. La colonne doit contenir toutes les données numériques, même s'il s'agit d'une colonne varchar2. Je dois trouver les enregistrements qui ne contiennent pas de données numériques (la fonction to_number (nom_col) renvoie une erreur lorsque j'essaie de l'appeler sur cette colonne).

15
Ben

Je pensais que vous pourriez utiliser une condition regexp_like et utiliser l'expression régulière pour rechercher les éléments non numériques. J'espère que cela pourrait aider?!

SELECT * FROM table_with_column_to_search WHERE REGEXP_LIKE(varchar_col_with_non_numerics, '[^0-9]+');
18
SGB

Pour obtenir un indicateur:

DECODE( TRANSLATE(your_number,' 0123456789',' ')

par exemple.

SQL> select DECODE( TRANSLATE('12345zzz_not_numberee',' 0123456789',' '), NULL, 'number','contains char')
 2 from dual
 3 /

"contains char"

et

SQL> select DECODE( TRANSLATE('12345',' 0123456789',' '), NULL, 'number','contains char')
 2 from dual
 3 /

"number"

et

SQL> select DECODE( TRANSLATE('123405',' 0123456789',' '), NULL, 'number','contains char')
 2 from dual
 3 /

"number"

Oracle 11g a des expressions régulières, vous pouvez donc utiliser ceci pour obtenir le nombre réel :

SQL> SELECT colA
  2  FROM t1
  3  WHERE REGEXP_LIKE(colA, '[[:digit:]]');

COL1
----------
47845
48543
12
...

S'il existe une valeur non numérique telle que '23g', elle sera simplement ignorée.

11
Michael Durrant

Contrairement à la réponse de SGB, je préfère définir l'expression rationnelle en définissant le format réel de mes données et en l'annulant. Cela me permet de définir des valeurs telles que $ DDD, DDD, DDD.DD Dans le scénario simple des PO, cela ressemblerait à

SELECT * 
FROM table_with_column_to_search 
WHERE NOT REGEXP_LIKE(varchar_col_with_non_numerics, '^[0-9]+$');

qui trouve tous les entiers non positifs. Si vous acceptez également des entiers négatifs, c’est un changement simple, il suffit d’ajouter un signe d’avant moins.

SELECT * 
FROM table_with_column_to_search 
WHERE NOT REGEXP_LIKE(varchar_col_with_non_numerics, '^-?[0-9]+$');

accepter des points flottants ...

SELECT * 
FROM table_with_column_to_search 
WHERE NOT REGEXP_LIKE(varchar_col_with_non_numerics, '^-?[0-9]+(\.[0-9]+)?$');

Même va plus loin avec n'importe quel format. Fondamentalement, vous aurez généralement déjà les formats pour valider les données d’entrée. Ainsi, lorsque vous aurez envie de trouver des données qui ne correspondent pas à ce format ... il est plus simple de nier ce format que d’en créer un autre; ce qui dans le cas de l'approche de SGB serait un peu difficile à faire si vous voulez plus que des entiers positifs.

5
ciuly

Utilisez ceci

SELECT * 
FROM TableToSearch 
WHERE NOT REGEXP_LIKE(ColumnToSearch, '^-?[0-9]+(\.[0-9]+)?$');
4
Anil

De http://www.dba-Oracle.com/t_isnumeric.htm

LENGTH(TRIM(TRANSLATE(, ' +-.0123456789', ' '))) is null

S'il reste quelque chose dans la chaîne après TRIM, il doit s'agir de caractères non numériques.

1
capitano666

Après avoir fait quelques essais, en s'appuyant sur les suggestions des réponses précédentes, il semble y avoir deux solutions utilisables.

La méthode 1 est la plus rapide, mais moins puissante en termes de correspondance de modèles plus complexes.
La méthode 2 est plus flexible, mais plus lente.

Méthode 1 - le plus rapide
J'ai testé cette méthode sur une table comportant 1 million de lignes.
Il semble être 3,8 fois plus rapide que les solutions regex.
Le remplacement-0 résout le problème selon lequel 0 est mappé sur un espace et ne semble pas ralentir la requête.

SELECT *
FROM <table>
WHERE TRANSLATE(replace(<char_column>,'0',''),'0123456789',' ') IS NOT NULL;

Méthode 2 - plus lente, mais plus flexible
J'ai comparé la vitesse de mise de la négation à l'intérieur ou à l'extérieur de l'expression regex. Les deux sont également plus lents que la solution de traduction. En conséquence, l’approche de @ ciuly semble plus judicieuse lorsqu’on utilise regex.

SELECT *
FROM <table>
WHERE NOT REGEXP_LIKE(<char_column>, '^[0-9]+$');
0
Wouter

Vous pouvez utiliser ce chèque:

create or replace function to_n(c varchar2) return number is
begin return to_number(c);
exception when others then return -123456;
end;

select id, n from t where to_n(n) = -123456;
0
egor7

J'ai trouvé cela utile:

 select translate('your string','_0123456789','_') from dual

Si le résultat est NULL, il est numérique (en ignorant les nombres à virgule flottante.)

Cependant, je suis un peu perplexe quant à la nécessité du trait de soulignement. Sans cela, les éléments suivants renvoient également null:

 select translate('s123','0123456789', '') from dual

Il y a aussi un de mes trucs préférés - pas parfait si la chaîne contient des choses comme "*" ou "#":

 SELECT 'is a number' FROM dual WHERE UPPER('123') = LOWER('123')
0
aiGuru

Après avoir fait quelques tests, je suis venu avec cette solution, laissez-moi savoir au cas où cela aiderait.

Ajoutez ceci ci-dessous 2 conditions dans votre requête et il trouvera les enregistrements qui ne contiennent pas de données numériques

 and REGEXP_LIKE(<column_name>, '\D') -- this selects non numeric data
 and not REGEXP_LIKE(column_name,'^[-]{1}\d{1}') -- this filters out negative(-) values
0
JAY SOPARIYA