Rechercher une valeur spécifique dans tous les champs de toutes les tables (Oracle)


115

Est-il possible de rechercher dans chaque champ de chaque table une valeur particulière dans Oracle?

Il y a des centaines de tables avec des milliers de lignes dans certaines tables, donc je sais que cela peut prendre beaucoup de temps à interroger. Mais la seule chose que je sais, c'est qu'une valeur pour le champ sur lequel je voudrais interroger est 1/22/2008P09RR8. <

J'ai essayé d'utiliser cette déclaration ci-dessous pour trouver une colonne appropriée en fonction de ce que je pense qu'elle devrait être nommée, mais elle n'a renvoyé aucun résultat.

SELECT * from dba_objects 
WHERE object_name like '%DTN%'

Il n'y a absolument aucune documentation sur cette base de données et je n'ai aucune idée d'où ce champ est extrait.

Des pensées?


Pouvons-nous faire cela en utilisant une seule requête au lieu d'utiliser une procédure stockée?
Freakyuser


@LalitKumarB La page que vous avez répertoriée n'est plus accessible. Serait-il possible d'afficher des informations en guise de réponse?
Dodzi Dzakuma

@DodziDzakuma La page est accessible lalitkumarb.wordpress.com/2015/01/06/... Aussi j'ai posté une réponse, s'il vous plaît défiler vers le bas ou voir stackoverflow.com/a/27794127/3989608
Lalit Kumar B

Si vous ne parvenez pas à comprendre la requête Lalit Kumar, essayez cette démo: sqlfiddle.com/#!4/76924c/2/0
DxTx

Réponses:


93

Citation:

J'ai essayé d'utiliser cette déclaration ci-dessous pour trouver une colonne appropriée en fonction de ce que je pense qu'elle devrait être nommée, mais elle n'a renvoyé aucun résultat. *

SELECT * from dba_objects WHERE
object_name like '%DTN%'

Une colonne n'est pas un objet. Si vous voulez dire que vous vous attendez à ce que le nom de la colonne ressemble à '% DTN%', la requête souhaitée est:

SELECT owner, table_name, column_name FROM all_tab_columns WHERE column_name LIKE '%DTN%';

Mais si la chaîne 'DTN' n'est qu'une supposition de votre part, cela n'aidera probablement pas.

Au fait, dans quelle mesure êtes-vous certain que «1/22 / 2008P09RR8» est une valeur sélectionnée directement dans une seule colonne? Si vous ne savez pas du tout d'où il vient, il peut s'agir d'une concaténation de plusieurs colonnes, ou du résultat d'une fonction, ou d'une valeur située dans un objet table imbriqué. Donc, vous pourriez être sur une chasse à l'oie sauvage en essayant de vérifier chaque colonne pour cette valeur. Ne pouvez-vous pas commencer avec n'importe quelle application cliente affichant cette valeur et essayer de comprendre quelle requête elle utilise pour l'obtenir?

Quoi qu'il en soit, la réponse de diciu donne une méthode de génération de requêtes SQL pour vérifier la valeur de chaque colonne de chaque table. Vous pouvez également faire des choses similaires entièrement dans une session SQL en utilisant un bloc PL / SQL et SQL dynamique. Voici un code écrit à la hâte pour cela:

    SET SERVEROUTPUT ON SIZE 100000

    DECLARE
      match_count INTEGER;
    BEGIN
      FOR t IN (SELECT owner, table_name, column_name
                  FROM all_tab_columns
                  WHERE owner <> 'SYS' and data_type LIKE '%CHAR%') LOOP

        EXECUTE IMMEDIATE
          'SELECT COUNT(*) FROM ' || t.owner || '.' || t.table_name ||
          ' WHERE '||t.column_name||' = :1'
          INTO match_count
          USING '1/22/2008P09RR8';

        IF match_count > 0 THEN
          dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
        END IF;

      END LOOP;

    END;
    /

Il existe également des moyens de le rendre plus efficace.

Dans ce cas, compte tenu de la valeur que vous recherchez, vous pouvez clairement éliminer toute colonne de type NUMBER ou DATE, ce qui réduirait le nombre de requêtes. Peut-être même le restreindre aux colonnes où le type est comme '% CHAR%'.

Au lieu d'une requête par colonne, vous pouvez créer une requête par table comme ceci:

SELECT * FROM table1
  WHERE column1 = 'value'
     OR column2 = 'value'
     OR column3 = 'value'
     ...
     ;

Vous devez le limiter aux colonnes char, varchar et varchar2, car les colonnes number et date ne peuvent pas contenir cette chaîne.
Erich Kitzmueller

8
@ammoQ - comme je l'ai dit dans l'avant-dernier paragraphe?
Dave Costa

J'ai couru ceci sur 9i et j'obtiens une erreur inconnue nom_colonne. Quelqu'un peut-il me dire quelle modification sera nécessaire pour l'exécuter sur 9i?
Regmi

@ Regmi - désolé, c'était en fait une erreur dans mon code, pas un problème de version. La boucle aurait dû être pilotée par all_tab_columnsnon all_tables. Je l'ai réparé.
Dave Costa

@DaveCosta - Merci pour le correctif mais j'obtiens toujours l'erreur «table ou vue n'existe pas» à la ligne 6. La ligne 6 étant «Exécuter immédiatement».
Regmi

34

J'ai apporté quelques modifications au code ci-dessus pour le rendre plus rapide si vous recherchez un seul propriétaire. Il vous suffit de modifier les 3 variables v_owner, v_data_type et v_search_string pour qu'elles correspondent à ce que vous recherchez.

SET SERVEROUTPUT ON SIZE 100000

DECLARE
  match_count INTEGER;
-- Type the owner of the tables you are looking at
  v_owner VARCHAR2(255) :='ENTER_USERNAME_HERE';

-- Type the data type you are look at (in CAPITAL)
-- VARCHAR2, NUMBER, etc.
  v_data_type VARCHAR2(255) :='VARCHAR2';

-- Type the string you are looking at
  v_search_string VARCHAR2(4000) :='string to search here...';

BEGIN
  FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and data_type = v_data_type) LOOP

    EXECUTE IMMEDIATE 
    'SELECT COUNT(*) FROM '||t.table_name||' WHERE '||t.column_name||' = :1'
    INTO match_count
    USING v_search_string;

    IF match_count > 0 THEN
      dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
    END IF;

  END LOOP;
END;
/

J'ai dû commenter la première ligne pour pouvoir exécuter cette requête. De plus, je n'ai pas pu supprimer le filtre propriétaire et exécuter la requête.
Popa Andrei

1
J'avais besoin de mettre des guillemets doubles autour du nom de la table / du nom de la colonne pour éviter les problèmes lorsque ceux-ci doivent être cités:'SELECT COUNT(*) FROM "'||t.table_name||'" WHERE "'||t.column_name||'" = :1'
Steve Chambers

Attention, all_tab_cols contient également des vues, malgré le nom
phil_w

qu'est-ce que c'est exactement dbms_output? Parce que les requêtes sont exécutées avec succès dans DataGrip, mais je ne vois aucun résultat après.
misteeque le

Je sais que c'est un peu vieux, mais quand je l'exécute, je viens d'obtenir une sortie de script de "bloc anonyme terminé"
JasonWH

7

Voici une autre version modifiée qui comparera une correspondance de sous-chaîne inférieure. Cela fonctionne dans Oracle 11g.

DECLARE
  match_count INTEGER;
-- Type the owner of the tables you are looking at
  v_owner VARCHAR2(255) :='OWNER_NAME';

-- Type the data type you are look at (in CAPITAL)
-- VARCHAR2, NUMBER, etc.
  v_data_type VARCHAR2(255) :='VARCHAR2';

-- Type the string you are looking at
  v_search_string VARCHAR2(4000) :='%lower-search-sub-string%';

BEGIN
  FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and data_type = v_data_type) LOOP

    EXECUTE IMMEDIATE 
    'SELECT COUNT(*) FROM '||t.table_name||' WHERE lower('||t.column_name||') like :1'
    INTO match_count
    USING v_search_string;

    IF match_count > 0 THEN
      dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
    END IF;

  END LOOP;
END;
/

7

Oui, vous pouvez et votre DBA vous détestera et vous trouvera pour clouer vos chaussures au sol car cela entraînera beaucoup d'E / S et réduira vraiment les performances de la base de données à mesure que le cache se purge.

select column_name from all_tab_columns c, user_all_tables u where c.table_name = u.table_name;

pour un début.

Je commencerais par les requêtes en cours, en utilisant le v$sessionet le v$sqlarea. Cela change en fonction de la version d'Oracle. Cela réduira l'espace et ne touchera pas tout.


7

Je sais que c'est un vieux sujet. Mais je vois un commentaire à la question demandant si cela pourrait être fait SQLplutôt que d'utiliser PL/SQL. Alors pensé à poster une solution.

La démonstration ci-dessous consiste à rechercher une VALEUR dans toutes les COLONNES de toutes les TABLES d'un SCHEMA entier :

  • Rechercher un type de PERSONNAGE

Cherchons la valeur KINGdans le SCOTTschéma.

SQL> variable val varchar2(10)
SQL> exec :val := 'KING'

PL/SQL procedure successfully completed.

SQL> SELECT DISTINCT SUBSTR (:val, 1, 11) "Searchword",
  2    SUBSTR (table_name, 1, 14) "Table",
  3    SUBSTR (column_name, 1, 14) "Column"
  4  FROM cols,
  5    TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select '
  6    || column_name
  7    || ' from '
  8    || table_name
  9    || ' where upper('
 10    || column_name
 11    || ') like upper(''%'
 12    || :val
 13    || '%'')' ).extract ('ROWSET/ROW/*') ) ) t
 14  ORDER BY "Table"
 15  /

Searchword  Table          Column
----------- -------------- --------------
KING        EMP            ENAME

SQL>
  • Rechercher un type NUMERIC

Cherchons la valeur 20dans le SCOTTschéma.

SQL> variable val NUMBER
SQL> exec :val := 20

PL/SQL procedure successfully completed.

SQL> SELECT DISTINCT SUBSTR (:val, 1, 11) "Searchword",
  2    SUBSTR (table_name, 1, 14) "Table",
  3    SUBSTR (column_name, 1, 14) "Column"
  4  FROM cols,
  5    TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select '
  6    || column_name
  7    || ' from '
  8    || table_name
  9    || ' where upper('
 10    || column_name
 11    || ') like upper(''%'
 12    || :val
 13    || '%'')' ).extract ('ROWSET/ROW/*') ) ) t
 14  ORDER BY "Table"
 15  /

Searchword  Table          Column
----------- -------------- --------------
20          DEPT           DEPTNO
20          EMP            DEPTNO
20          EMP            HIREDATE
20          SALGRADE       HISAL
20          SALGRADE       LOSAL

SQL>

6
hrmm .... l'utilisation de xml semble exagérée. En outre:Error occurred in XML processing ORA-00932: inconsistent datatypes: expected NUMBER got BLOB
towi

2
ORA-19202: Une erreur s'est produite lors du traitement XML ORA-00932: types de données incohérents: le CHAR attendu a obtenu le BLOB ORA-06512: à "SYS.DBMS_XMLGEN", ligne 288 ORA-06512: à la ligne 1 19202. 00000 - "Une erreur s'est produite lors du traitement XML % s "* Cause: Une erreur s'est produite lors du traitement de la fonction XML * Action: Vérifiez le message d'erreur donné et corrigez le problème approprié
Mohammad Faisal

Des idées? ORA-19202: Une erreur s'est produite dans le traitement XML ORA-22813: la valeur de l'opérande dépasse les limites du système ORA-06512: à "SYS.DBMS_XMLGEN", ligne 288 ORA-06512: à la ligne 1
Menios

5

Je ferais quelque chose comme ça (génère toutes les sélections dont vous avez besoin). Vous pouvez plus tard les alimenter vers sqlplus:

echo "select table_name from user_tables;" | sqlplus -S user/pwd | grep -v "^--" | grep -v "TABLE_NAME" | grep "^[A-Z]" | while read sw;
do echo "desc $sw" | sqlplus -S user/pwd | grep -v "\-\-\-\-\-\-" | awk -F' ' '{print $1}' | while read nw;
do echo "select * from $sw where $nw='val'";
done;
done;

Il donne:

select * from TBL1 where DESCRIPTION='val'
select * from TBL1 where ='val'
select * from TBL2 where Name='val'
select * from TBL2 where LNG_ID='val'

Et ce qu'il fait est - pour chacun table_namede user_tablesobtenir chaque champ (de desc) et créer un select * from table où le champ est égal à «val».


5

J'ai modifié le script de Flood pour qu'il s'exécute une fois pour chaque table plutôt que pour chaque colonne de chaque table pour une exécution plus rapide. Il nécessite Oracle 11g ou supérieur.

    set serveroutput on size 100000

declare
    v_match_count integer;
    v_counter integer;

    -- The owner of the tables to search through (case-sensitive)
    v_owner varchar2(255) := 'OWNER_NAME';
    -- A string that is part of the data type(s) of the columns to search through (case-insensitive)
    v_data_type varchar2(255) := 'CHAR';
    -- The string to be searched for (case-insensitive)
    v_search_string varchar2(4000) := 'FIND_ME';

    -- Store the SQL to execute for each table in a CLOB to get around the 32767 byte max size for a VARCHAR2 in PL/SQL
    v_sql clob := '';
begin
    for cur_tables in (select owner, table_name from all_tables where owner = v_owner and table_name in 
                       (select table_name from all_tab_columns where owner = all_tables.owner and data_type like '%' ||  upper(v_data_type) || '%')
                       order by table_name) loop
        v_counter := 0;
        v_sql := '';

        for cur_columns in (select column_name from all_tab_columns where 
                            owner = v_owner and table_name = cur_tables.table_name and data_type like '%' || upper(v_data_type) || '%') loop
            if v_counter > 0 then
                v_sql := v_sql || ' or ';
            end if;
            v_sql := v_sql || 'upper(' || cur_columns.column_name || ') like ''%' || upper(v_search_string) || '%''';
            v_counter := v_counter + 1;
        end loop;

        v_sql := 'select count(*) from ' || cur_tables.table_name || ' where ' || v_sql;

        execute immediate v_sql
        into v_match_count;

        if v_match_count > 0 then
            dbms_output.put_line('Match in ' || cur_tables.owner || ': ' || cur_tables.table_name || ' - ' || v_match_count || ' records');
        end if;
    end loop;

    exception
        when others then
            dbms_output.put_line('Error when executing the following: ' || dbms_lob.substr(v_sql, 32600));
end;
/

5

J'avais les problèmes suivants pour la réponse @Lalit Kumars,

ORA-19202: Error occurred in XML processing
ORA-00904: "SUCCESS": invalid identifier
ORA-06512: at "SYS.DBMS_XMLGEN", line 288
ORA-06512: at line 1
19202. 00000 -  "Error occurred in XML processing%s"
*Cause:    An error occurred when processing the XML function
*Action:   Check the given error message and fix the appropriate problem

La solution est:

WITH  char_cols AS
  (SELECT /*+materialize */ table_name, column_name
   FROM   cols
   WHERE  data_type IN ('CHAR', 'VARCHAR2'))
SELECT DISTINCT SUBSTR (:val, 1, 11) "Searchword",
       SUBSTR (table_name, 1, 14) "Table",
       SUBSTR (column_name, 1, 14) "Column"
FROM   char_cols,
       TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select "'
       || column_name
       || '" from "'
       || table_name
       || '" where upper("'
       || column_name
       || '") like upper(''%'
       || :val
       || '%'')' ).extract ('ROWSET/ROW/*') ) ) t
ORDER  BY "Table"
/ 

4

si nous connaissons les noms de table et de colonne mais que nous voulons savoir le nombre de fois que la chaîne apparaît pour chaque schéma:

Declare

owner VARCHAR2(1000);
tbl VARCHAR2(1000);
cnt number;
ct number;
str_sql varchar2(1000);
reason varchar2(1000);
x varchar2(1000):='%string_to_be_searched%';

cursor csr is select owner,table_name 
from all_tables where table_name ='table_name';

type rec1 is record (
ct VARCHAR2(1000));

type rec is record (
owner VARCHAR2(1000):='',
table_name VARCHAR2(1000):='');

rec2 rec;
rec3 rec1;
begin

for rec2 in csr loop

--str_sql:= 'select count(*) from '||rec.owner||'.'||rec.table_name||' where CTV_REMARKS like '||chr(39)||x||chr(39);
--dbms_output.put_line(str_sql);
--execute immediate str_sql

execute immediate 'select count(*) from '||rec2.owner||'.'||rec2.table_name||' where column_name like '||chr(39)||x||chr(39)
into rec3;
if rec3.ct <> 0 then
dbms_output.put_line(rec2.owner||','||rec3.ct);
else null;
end if;
end loop;
end;

2

Procédure de recherche dans toute la base de données:

    CREATE or REPLACE PROCEDURE SEARCH_DB(SEARCH_STR IN VARCHAR2, TAB_COL_RECS OUT VARCHAR2) IS
      match_count integer;
      qry_str varchar2(1000);
      CURSOR TAB_COL_CURSOR IS 
          SELECT TABLE_NAME,COLUMN_NAME,OWNER,DATA_TYPE FROM ALL_TAB_COLUMNS WHERE DATA_TYPE in ('NUMBER','VARCHAR2') AND OWNER='SCOTT';
          BEGIN  
            FOR TAB_COL_REC  IN TAB_COL_CURSOR
            LOOP
              qry_str := 'SELECT COUNT(*) FROM '||TAB_COL_REC.OWNER||'.'||TAB_COL_REC.TABLE_NAME|| 
              ' WHERE '||TAB_COL_REC.COLUMN_NAME;
               IF TAB_COL_REC.DATA_TYPE = 'NUMBER' THEN
                      qry_str := qry_str||'='||SEARCH_STR; 
               ELSE
                       qry_str := qry_str||' like '||SEARCH_STR; 
               END IF;
                       --dbms_output.put_line( qry_str );
                EXECUTE IMMEDIATE  qry_str  INTO match_count;
                IF match_count > 0 THEN          
                   dbms_output.put_line( qry_str );
                  --dbms_output.put_line( TAB_COL_REC.TABLE_NAME ||' '||TAB_COL_REC.COLUMN_NAME ||' '||match_count);     
                    TAB_COL_RECS := TAB_COL_RECS||'@@'||TAB_COL_REC.TABLE_NAME||'##'||TAB_COL_REC.COLUMN_NAME;
                END IF; 
          END LOOP;
     END SEARCH_DB;    

Exécuter l'instruction

  DECLARE
    SEARCH_STR VARCHAR2(200);
    TAB_COL_RECS VARCHAR2(200);
    BEGIN
      SEARCH_STR := 10;
      SEARCH_DB(
        SEARCH_STR => SEARCH_STR,
        TAB_COL_RECS => TAB_COL_RECS
      );
     DBMS_OUTPUT.PUT_LINE('TAB_COL_RECS = ' || TAB_COL_RECS);
     END;

Exemples de résultats

Connecting to the database test.
SELECT COUNT(*) FROM SCOTT.EMP WHERE DEPTNO=10
SELECT COUNT(*) FROM SCOTT.DEPT WHERE DEPTNO=10
TAB_COL_RECS = @@EMP##DEPTNO@@DEPT##DEPTNO
Process exited.
Disconnecting from the database test.

1

Je n'ai pas de solution simple sur le promprt SQL. Cependant, il existe de nombreux outils comme crapaud et PL / SQL Developer qui ont une interface graphique dans laquelle un utilisateur peut entrer la chaîne à rechercher et il renverra la table / procédure / objet où cela se trouve.



1

Modifier le code pour rechercher sans tenir compte de la casse à l'aide d'une requête LIKE au lieu de trouver des correspondances exactes ...

DECLARE
  match_count INTEGER;
  -- Type the owner of the tables you want to search.
  v_owner VARCHAR2(255) :='USER';
  -- Type the data type you're looking for (in CAPS). Examples include: VARCHAR2, NUMBER, etc.
  v_data_type VARCHAR2(255) :='VARCHAR2';
  -- Type the string you are looking for.
  v_search_string VARCHAR2(4000) :='Test';
BEGIN
  dbms_output.put_line( 'Starting the search...' );
  FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and data_type = v_data_type) LOOP
    EXECUTE IMMEDIATE 
    'SELECT COUNT(*) FROM '||t.table_name||' WHERE LOWER('||t.column_name||') LIKE :1'
    INTO match_count
    USING LOWER('%'||v_search_string||'%');
    IF match_count > 0 THEN
      dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
    END IF;
  END LOOP;
END;

0

--it exécution terminée - pas d'erreur

    SET SERVEROUTPUT ON SIZE 100000

DECLARE
   v_match_count     INTEGER;
   v_counter         INTEGER;




v_owner           VARCHAR2 (255) := 'VASOA';
v_search_string   VARCHAR2 (4000) := '99999';
v_data_type       VARCHAR2 (255) := 'CHAR';
v_sql             CLOB := '';

BEGIN
   FOR cur_tables
      IN (  SELECT owner, table_name
              FROM all_tables
             WHERE     owner = v_owner
                   AND table_name IN (SELECT table_name
                                        FROM all_tab_columns
                                       WHERE     owner = all_tables.owner
                                             AND data_type LIKE
                                                       '%'
                                                    || UPPER (v_data_type)
                                                    || '%')
          ORDER BY table_name)
   LOOP
      v_counter := 0;
      v_sql := '';

      FOR cur_columns
         IN (SELECT column_name, table_name
               FROM all_tab_columns
              WHERE     owner = v_owner
                    AND table_name = cur_tables.table_name
                    AND data_type LIKE '%' || UPPER (v_data_type) || '%')
      LOOP
         IF v_counter > 0
         THEN
            v_sql := v_sql || ' or ';
         END IF;

         IF cur_columns.column_name is not null
         THEN
            v_sql :=
                  v_sql
               || 'upper('
               || cur_columns.column_name
               || ') ='''
               || UPPER (v_search_string)||'''';

            v_counter := v_counter + 1;
         END IF;

      END LOOP;

      IF v_sql is  null
      THEN
         v_sql :=
               'select count(*) from '
            || v_owner
            || '.'
            || cur_tables.table_name;

      END IF;

      IF v_sql is not null
      THEN
         v_sql :=
               'select count(*) from '
            || v_owner
            || '.'
            || cur_tables.table_name
            || ' where '
            || v_sql;
      END IF;

      --v_sql := 'select count(*) from ' ||v_owner||'.'|| cur_tables.table_name ||' where '||  v_sql;


      --dbms_output.put_line(v_sql);
      --DBMS_OUTPUT.put_line (v_sql);

      EXECUTE IMMEDIATE v_sql INTO v_match_count;

      IF v_match_count > 0
      THEN
        DBMS_OUTPUT.put_line (v_sql);
        dbms_output.put_line('Match in ' || cur_tables.owner || ': ' || cur_tables.table_name || ' - ' || v_match_count || ' records');
      END IF;

   END LOOP;
EXCEPTION
   WHEN OTHERS
   THEN
      DBMS_OUTPUT.put_line (
            'Error when executing the following: '
         || DBMS_LOB.SUBSTR (v_sql, 32600));
END;
/

0

Emprunter, améliorer légèrement et simplifier à partir de cet article de blog, la simple instruction SQL suivante semble très bien faire le travail:

SELECT DISTINCT (:val) "Search Value", TABLE_NAME "Table", COLUMN_NAME "Column"
FROM cols,
     TABLE (XMLSEQUENCE (DBMS_XMLGEN.GETXMLTYPE(
       'SELECT "' || COLUMN_NAME || '" FROM "' || TABLE_NAME || '" WHERE UPPER("'
       || COLUMN_NAME || '") LIKE UPPER(''%' || :val || '%'')' ).EXTRACT ('ROWSET/ROW/*')))
ORDER BY "Table";

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.