Buscar todos los campos en todas las tablas para un valor específico (Oracle)

115

¿Es posible buscar en cada campo de cada tabla un valor particular en Oracle?

Hay cientos de tablas con miles de filas en algunas tablas, así que sé que la consulta puede llevar mucho tiempo. Pero lo único que sé es que un valor para el campo que me gustaría consultar es 1/22/2008P09RR8. <

Intenté usar esta declaración a continuación para encontrar una columna apropiada en función de lo que creo que debería llamarse, pero no arrojó resultados.

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

No hay absolutamente ninguna documentación en esta base de datos y no tengo idea de dónde se extrae este campo.

¿Alguna idea?

Chris Conway
fuente
¿Podemos hacer esto usando una sola consulta en lugar de usar un procedimiento almacenado?
Freakyuser
Sí, es posible hacerlo en SQL puro. Consulte SQL para buscar un VALOR en todas las COLUMNAS de todas las TABLAS en un ESQUEMA completo
Lalit Kumar B
@LalitKumarB La página que enumeraste ya no es accesible. ¿Sería posible publicar alguna información como respuesta?
Dodzi Dzakuma
@DodziDzakuma La página es accesible lalitkumarb.wordpress.com/2015/01/06/… También he publicado una respuesta, desplácese hacia abajo o consulte stackoverflow.com/a/27794127/3989608
Lalit Kumar B
Si tiene problemas para resolver la consulta de Lalit Kumar, pruebe esta demostración: sqlfiddle.com/#!4/76924c/2/0
DxTx

Respuestas:

93

Citar:

Intenté usar esta declaración a continuación para encontrar una columna adecuada en función de lo que creo que debería llamarse, pero no arrojó resultados. *

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

Una columna no es un objeto. Si quiere decir que espera que el nombre de la columna sea como '% DTN%', la consulta que desea es:

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

Pero si la cadena 'DTN' es solo una suposición de su parte, eso probablemente no ayudará.

Por cierto, ¿qué tan seguro está de que '1/22 ​​/ 2008P09RR8' es un valor seleccionado directamente de una sola columna? Si no sabe en absoluto de dónde proviene, podría ser una concatenación de varias columnas, o el resultado de alguna función, o un valor que se encuentra en un objeto de tabla anidado. Por lo tanto, es posible que esté en una búsqueda inútil tratando de verificar cada columna para ese valor. ¿No puede comenzar con cualquier aplicación cliente que muestre este valor e intentar averiguar qué consulta está utilizando para obtenerlo?

De todos modos, la respuesta de diciu ofrece un método para generar consultas SQL para verificar el valor de cada columna de cada tabla. También puede hacer cosas similares por completo en una sesión de SQL utilizando un bloque PL / SQL y SQL dinámico. Aquí hay un código escrito apresuradamente para eso:

    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;
    /

Hay algunas formas de hacerlo más eficiente también.

En este caso, dado el valor que estás buscando, claramente puedes eliminar cualquier columna que sea del tipo NUMBER o DATE, lo que reduciría el número de consultas. Quizás incluso restringirlo a columnas donde el tipo es como '% CHAR%'.

En lugar de una consulta por columna, puede crear una consulta por tabla como esta:

SELECT * FROM table1
  WHERE column1 = 'value'
     OR column2 = 'value'
     OR column3 = 'value'
     ...
     ;
Dave Costa
fuente
Debe restringirlo a las columnas char, varchar y varchar2, ya que las columnas de fecha y número no pueden contener esa cadena.
Erich Kitzmueller
8
@ammoQ: ¿como dije en el penúltimo párrafo?
Dave Costa
Ejecuté esto en 9i y obtengo el error desconocido column_name. ¿Alguien puede decirme qué modificación se necesitará para ejecutar esto en 9i?
Regmi
@Regmi: lo siento, en realidad fue un error en mi código, no un problema de versión. El bucle debería haber sido impulsado por all_tab_columnsno all_tables. Lo he arreglado.
Dave Costa
@DaveCosta - Gracias por la corrección, pero todavía obtengo el error 'la tabla o la vista no existe' en la línea 6. La línea 6 es "Ejecutar inmediatamente".
Regmi
34

Hice algunas modificaciones al código anterior para que funcione más rápido si solo está buscando un propietario. Solo tiene que cambiar las 3 variables v_owner, v_data_type y v_search_string para que se ajusten a lo que está buscando.

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;
/
Inundar
fuente
Tuve que comentar la primera línea para poder ejecutar esta consulta. Además, no pude eliminar el filtro de propietario y ejecutar la consulta.
Popa Andrei
1
Necesitaba poner comillas dobles alrededor del nombre de la tabla / nombre de la columna para evitar problemas cuando es necesario citarlos:'SELECT COUNT(*) FROM "'||t.table_name||'" WHERE "'||t.column_name||'" = :1'
Steve Chambers
Tenga en cuenta que all_tab_cols también contiene vistas, a pesar del nombre
phil_w
¿qué es exactamente dbms_output? Porque las consultas se ejecutan con éxito en DataGrip, pero no veo ningún resultado después.
misteeque
Sé que esto es un poco antiguo, pero cuando ejecuto esto, obtengo una salida de script de "bloque anónimo completado"
JasonWH
7

Aquí hay otra versión modificada que comparará una coincidencia de subcadena inferior. Esto funciona en 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;
/
xojins
fuente
7

Sí, puede y su DBA lo odiará y encontrará que clava sus zapatos en el piso porque eso causará muchas E / S y reducirá el rendimiento de la base de datos a medida que se purga la caché.

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

para comenzar.

Comenzaría con las consultas en ejecución, usando v$sessiony v$sqlarea. Esto cambia según la versión de Oracle. Esto reducirá el espacio y no afectará a todo.

Jim
fuente
7

Sé que este es un tema antiguo. Pero veo un comentario a la pregunta que pregunta si se podría hacer en SQLlugar de usar PL/SQL. Así que pensé en publicar una solución.

La siguiente demostración es buscar un VALOR en todas las COLUMNAS de todas las TABLAS en un ESQUEMA completo :

  • Buscar un tipo de CARÁCTER

Busquemos el valor KINGen el SCOTTesquema.

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>
  • Buscar un tipo NUMÉRICO

Busquemos el valor 20en el SCOTTesquema.

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>
Lalit Kumar B
fuente
6
hrmm .... usar xml parece una exageración. Además:Error occurred in XML processing ORA-00932: inconsistent datatypes: expected NUMBER got BLOB
towi
2
ORA-19202: Se produjo un error en el procesamiento de XML ORA-00932: Tipos de datos inconsistentes: el CHAR esperado obtuvo BLOB ORA-06512: en "SYS.DBMS_XMLGEN", línea 288 ORA-06512: en la línea 1 19202. 00000 - "Se produjo un error en el procesamiento de XML % s "* Causa: Se produjo un error al procesar la función XML * Acción: Verifique el mensaje de error dado y solucione el problema apropiado
Mohammad Faisal
¿Algunas ideas? ORA-19202: Se produjo un error en el procesamiento de XML ORA-22813: el valor del operando excede los límites del sistema ORA-06512: en "SYS.DBMS_XMLGEN", línea 288 ORA-06512: en la línea 1
Menios
5

Haría algo como esto (genera todas las selecciones que necesita). Más tarde puede alimentarlos a 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;

Cede:

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

Y lo que hace es - para cada uno table_namede user_tablesconseguir cada campo (de desc) y crear un selecto * de la tabla donde el campo es igual a 'val'.

diciu
fuente
5

Modifiqué el script de Flood para que se ejecute una vez para cada tabla en lugar de para cada columna de cada tabla para una ejecución más rápida. Requiere Oracle 11g o superior.

    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;
/
Mike Rodey
fuente
5

Estaba teniendo problemas para la respuesta de @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 solución es:

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"
/ 
AKB
fuente
4

si conocemos los nombres de la tabla y de las columnas, pero queremos saber el número de veces que aparece una cadena para cada esquema:

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;
umesh
fuente
2

Procedimiento para buscar en toda la base de datos:

    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;    

Ejecutar declaración

  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;

Resultados de muestra

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.
Hemanth
fuente
1

No tengo una solución simple en el indicador SQL. Sin embargo, hay bastantes herramientas como toad y PL / SQL Developer que tienen una GUI donde un usuario puede ingresar la cadena que se buscará y devolverá la tabla / procedimiento / objeto donde se encuentra.

Dheer
fuente
1

Hay algunas herramientas gratuitas que hacen este tipo de búsqueda, por ejemplo, esta funciona bien y el código fuente está disponible: https://sites.google.com/site/freejansoft/dbsearch

Necesitará el controlador ODBC de Oracle y un DSN para utilizar esta herramienta.

Juan
fuente
1

Modificar el código para buscar sin distinción entre mayúsculas y minúsculas utilizando una consulta LIKE en lugar de buscar coincidencias exactas ...

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;
Alexandru
fuente
0

--se ejecuta completado - sin error

    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;
/
iCrazybest
fuente
0

Tomar prestado, mejorar ligeramente y simplificar de esta publicación del blog la siguiente declaración SQL simple parece hacer el trabajo bastante bien:

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";
Steve Cámaras
fuente
-1
SELECT * from all_objects WHERE object_name like '%your_string%';
Rama Krishna
fuente