2017-05-28 2 views
1

J'essaye d'aller chercher tous les caractères ASCII non-imprimables du champ DESCRIPTION dans une table en utilisant SQL dans TOAD mais la requête ci-dessous ne fonctionne pas .afficher les caractères ascii non imprimables en SQL comme: ascii: ou: print: ne fonctionne pas

select 
regexp_instr(a.description,'[^[:ascii:]]') as description from 
poline a where a.ponum='XXX' and a.siteid='YYY' and 
regexp_instr(a.description,'[^[:ascii:]]') > 0 

la requête ci-dessus erreur acheté ORA-127729: classe de caractères invalides dans l'expression régulière. J'ai essayé: print: au lieu de: ascii: cependant il n'a apporté aucun résultat. Voici la description de cet enregistrement qui a des caractères non imprimables.

Sherlock 16 x 6.5” Wide Wheelbarrow wheel .M100P.10R – Effluent care bacteria and enzyme formulation 
+0

Merci max092012. Cherchez-vous à obtenir chaque occurrence d'un caractère non imprimable par rangée, ou seulement la première occurrence? – alexgibbs

Répondre

1

:ascii: n'est pas une classe de caractère valide, et même si elle l'était, il ne semble pas être ce que vous essayez d'obtenir ici (ascii ne contient des caractères non-imprimables). Les classes valides peuvent être trouvées here.

En fait, si vous remplacez :ascii: par :print: dans votre requête d'origine, il retournera en effet la première position dans chaque POLINE.DESCRIPTION qui est un caractère non imprimable. (Si elle ne retourne rien pour vous, il peut être parce que vos données DESCRIPTION est en fait tout imprimer.)

Mais comme vous l'avez dit vous voulez identifier Chaque omble non imprimable dans chaque DESCRIPTION dans POLINE, quelques changements serait nécessaire. Je vais inclure un exemple qui obtient chaque match comme point de départ.

Dans cet exemple, chaque DESCRIPTION sera décomposé en ses caractères constitutifs individuels, et chaque caractère sera vérifié pour l'imprimabilité. L'emplacement dans la chaîne DESCRIPTION avec le ASCII number du caractère non imprimable sera renvoyé.

Cet exemple suppose qu'il existe un identificateur unique pour chaque ligne au POLINE, appelé ici POLINE_ID.

Tout d'abord, créez la table de test:

CREATE TABLE POLINE(
    POLINE_ID NUMBER PRIMARY KEY, 
    PONUM VARCHAR2(32), 
    SITEID VARCHAR2(32), 
    DESCRIPTION VARCHAR2(256) 
); 

et charger des données. J'ai inséré quelques caractères non-impression dans l'exemple Sherlock chaîne que vous avez fourni, #23 et #17. Un exemple de chaîne composée uniquement des 64 premiers caractères ASCII (dont les 31 premiers ne figurent pas dans :print:) est également inclus, et certains remplisseurs sont inclus dans les prédicats PONUM et SITEID.

INSERT INTO POLINE VALUES (1,'XXX','YYY','Sherlock'||CHR(23)||' 16 x 6.5” Wide Wheelbarrow wheel .M100P.10R –'||CHR(17)||' Effluent care bacteria and enzyme formulation'); 

DECLARE 
    V_STRING VARCHAR2(64) := CHR(1); 
BEGIN 
    FOR POINTER IN 2..64 LOOP 
    V_STRING := V_STRING||CHR(POINTER); 
    END LOOP; 
    INSERT INTO POLINE VALUES (2, 'XXX','YYY',V_STRING); 
    INSERT INTO POLINE VALUES (3, 'AAA','BBB',V_STRING); 
END; 
/

INSERT INTO POLINE VALUES(4,'XXX','YYY','VOLTRON'); 

Maintenant, nous avons 4 lignes au total. Trois d'entre eux contiennent (plusieurs) caractères non imprimables, mais seulement deux d'entre eux doivent correspondre à toutes les restrictions.

Ensuite, lancez une requête. Il y a deux exemples de requêtes ci-dessous - le premier utilise REGEXP_INSTR avec comme dans votre exemple de requête initial (en remplaçant :cntrl: par :print:). Mais pour une alternative, une deuxième variante est également incluse qui vérifie simplement si chaque char est dans les 31 premiers caractères ascii.

Les deux exemples de requêtes indexeront chaque caractère de DESCRIPTION, et vérifieront s'il est imprimable, et collecteront le numéro ASCII et l'emplacement de chaque caractère non imprimable dans chaque candidat DESCRIPTION. La table d'exemple a ici DESCRIPTION s qui ont 256 caractères de long, donc ceci est utilisé comme index maximum dans la jointure cartésienne.

S'il vous plaît noter, ce sont pas efficaces, et sont conçus pour obtenir chaque match. Si vous n'avez besoin que de la première correspondance afterall, votre requête originale remplacée par :print: fonctionnera beaucoup mieux. En outre, cela peut également être réglé en passant dans PL/SOL ou peut-être en se récursif (si PL/SQL est autorisé dans votre cas d'utilisation, ou si vous êtes 11gR2 +, etc.). De plus, certains prédicats tels que REGEXP_LIKE n'ont pas d'impact sur le résultat final et ne servent qu'à permettre une filtration préliminaire. Ceux-ci pourraient être superflus (ou pire) pour vous, en fonction de votre ensemble de données.

Premier exemple, utilisant l'expression rationnelle et :print:

SELECT 
    POLINE_ID, 
    STRING_INDEX                  AS NON_PRINTABLE_LOCATION, 
    ASCII(REGEXP_SUBSTR(SUBSTR(DESCRIPTION, STRING_INDEX, 1), '[[:cntrl:]]', 1, 1)) AS NON_PRINTABLE_ASCII_NUMBER 
FROM POLINE 
    CROSS JOIN (SELECT LEVEL AS STRING_INDEX 
       FROM DUAL 
       CONNECT BY LEVEL < 257) CANDIDATE_LOCATION 
WHERE PONUM = 'XXX' 
     AND SITEID = 'YYY' 
     AND REGEXP_LIKE(DESCRIPTION, '[[:cntrl:]]') 
     AND REGEXP_INSTR(SUBSTR(DESCRIPTION, STRING_INDEX, 1), '[[:cntrl:]]', 1, 1, 0) > 0 
     AND STRING_INDEX <= LENGTH(DESCRIPTION) 
ORDER BY 1 ASC, 2 ASC; 

Deuxième exemple, en utilisant les numéros ASCII:

SELECT 
    POLINE_ID, 
    STRING_INDEX        AS NON_PRINTABLE_LOCATION, 
    ASCII(SUBSTR(DESCRIPTION, STRING_INDEX, 1)) AS NON_PRINTABLE_ASCII_NUMBER 
FROM POLINE 
    CROSS JOIN (SELECT LEVEL AS STRING_INDEX 
       FROM DUAL 
       CONNECT BY LEVEL < 257) CANDIDATE_LOCATION 
WHERE PONUM = 'XXX' 
     AND SITEID = 'YYY' 
     AND REGEXP_LIKE(DESCRIPTION, '[[:cntrl:]]') 
     AND ASCII(SUBSTR(DESCRIPTION, STRING_INDEX, 1)) BETWEEN 1 AND 31 
     AND STRING_INDEX <= LENGTH(DESCRIPTION) 
ORDER BY 1 ASC, 2 ASC; 

Dans nos données de test, ces requêtes produira une sortie équivalente. Nous devrions nous attendre à ce que cela ait deux hits (pour chrs 17 et 23) dans le SherlockDESCRIPTION, et 31 hits pour le premier-64-ascii DESCRIPTION.

Résultat:

POLINE_ID NON_PRINTABLE_LOCATION NON_PRINTABLE_ASCII_NUMBER 
1   9      23       
1   56      17       
2   1      1       
2   2      2       
2   3      3       
2   4      4       
2   5      5       
2   6      6       
2   7      7       
2   8      8       
2   9      9       
2   10      10       
2   11      11       
2   12      12       
2   13      13       
2   14      14       
2   15      15       
2   16      16       
2   17      17       
2   18      18       
2   19      19       
2   20      20       
2   21      21       
2   22      22       
2   23      23       
2   24      24       
2   25      25       
2   26      26       
2   27      27       
2   28      28       
2   29      29       
2   30      30       
2   31      31      

33 rows selected. 

EDIT En réponse aux commentaires, voici quelques précisions sur ce que nous pouvons attendre de [[:cntrl:]] et [^[:cntrl:]] avec regexp_instr.

[[:cntrl:]] correspond à tout des 31 premiers caractères ascii, alors que [^[:cntrl:]] est la négation logique de [[:cntrl:]], il correspondra tout sauf les 31 premiers caractères ascii.
Pour les comparer, nous pouvons commencer par le cas le plus simple d'un seul caractère, ascii #31. Comme il n'y a qu'un seul caractère, le résultat ne peut être que match ou miss. On attend ce qui suit pour revenir 1 pour le match:

SELECT REGEXP_INSTR(CHR(31),'[[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL; 

MATCH_INDEX 
1 

Mais 0 pour la miss avec la négation [^ [: cntrl:]]:

SELECT REGEXP_INSTR(CHR(31),'[^[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL; 

MATCH_INDEX 
0 

Maintenant, si l'on inclut deux (ou plus) caractères qui sont un mélange de imprimable et non imprimable, il y a plus de résultats possibles. Les deux[[:cntrl:]] et [^[:cntrl:]] peuvent correspondre, mais ils ne peuvent correspondre à des choses différentes. Si nous passons seulement de l'ascii #31 à l'ascii #64#31, nous nous attendons toujours à ce que [[:cntrl:]] corresponde (puisqu'il y a un caractère non-imprimable dans la deuxième position) mais il devrait maintenant retourner 2, puisque le non-imprimable est dans la deuxième position.

SELECT REGEXP_INSTR(CHR(64)||CHR(31),'[[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL; 

MATCH_INDEX 
2 

Et maintenant [^[:cntrl:]]aussi a la possibilité de faire correspondre (à la première position):

SELECT REGEXP_INSTR(CHR(64)||CHR(31),'[^[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL; 

MATCH_INDEX 
1 

Quand il y a un mélange de caractères imprimables et de contrôle, à la fois [[:cntrl:]] et [^[:cntrl:]] peuvent correspondre à, mais ils vont correspondre à différents indices.

+0

merci beaucoup pour l'explication détaillée. Mon exigence est de choisir des caractères ascii non imprimables dans n'importe quelle position dans le champ de description. Je suis tombé sur un test et [] [[cntrl:]] fonctionne cependant [[: cntrl:]] + fonctionne aussi mais ils apportent tous deux des résultats différents. Pouvez-vous s'il vous plaît élaborer la différence entre les mêmes? Merci encore!! – max092012

+0

Merci @ max092012 Ça va prendre quelques heures (tard dans mon fuseau horaire) mais je suis sûr que je ferai un suivi sur [^ [: cntrl:]] vs [[: cntrl:]]. Le matcher efficace généré par ceux-ci est en effet différent, il semble donc raisonnable qu'ils produiraient des résultats différents. Dans votre cas, je crois que [: cntrl:] sont vos cibles. Je ferai un suivi après en avoir ajouté quelques-unes sur ces classes de personnages. Merci – alexgibbs

+0

Merci @ max092012 J'ai ajouté une section en bas du post sur le comportement de 'regexp_instr' avec' [[: cntrl:]] 'vs' [^ [: cntrl:]] 'contre différentes combinaisons de caractères. Est-ce que cela aide à clarifier les requêtes? Merci – alexgibbs