Pesquisar todos os campos em todas as tabelas por um valor específico (Oracle)

115

É possível pesquisar cada campo de cada tabela por um valor específico no Oracle?

Existem centenas de tabelas com milhares de linhas em algumas tabelas, então eu sei que a consulta pode levar muito tempo. Mas a única coisa que sei é que um valor para o campo que eu gostaria de consultar é1/22/2008P09RR8 . <

Tentei usar a instrução abaixo para encontrar uma coluna apropriada com base no que acho que deveria ser nomeada, mas não retornou nenhum resultado.

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

Não há absolutamente nenhuma documentação neste banco de dados e não tenho ideia de onde esse campo está sendo obtido.

Alguma ideia?

Chris Conway
fonte
Podemos fazer isso usando uma única consulta em vez de usar um procedimento armazenado?
Freakyuser
Sim, é possível fazer isso em SQL puro. Consulte SQL para pesquisar um VALOR em todas as COLUNAS de todas as TABELAS em um ESQUEMA inteiro
Lalit Kumar B
@LalitKumarB A página que você listou não está mais acessível. Seria possível postar alguma informação como resposta?
Dodzi Dzakuma
@DodziDzakuma A página está acessível lalitkumarb.wordpress.com/2015/01/06/… Também postei uma resposta, role para baixo ou consulte stackoverflow.com/a/27794127/3989608
Lalit Kumar B
Se você estiver tendo problemas para descobrir a consulta de Lalit Kumar, tente esta demonstração: sqlfiddle.com/#!4/76924c/2/0
DxTx

Respostas:

93

Citar:

Tentei usar a instrução abaixo para encontrar uma coluna apropriada com base no que acho que deveria ser nomeada, mas não retornou resultados. *

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

Uma coluna não é um objeto. Se você quer dizer que espera que o nome da coluna seja como '% DTN%', a consulta desejada é:

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

Mas se a string 'DTN' for apenas uma suposição de sua parte, isso provavelmente não ajudará.

A propósito, como você tem certeza de que '1/22 ​​/ 2008P09RR8' é um valor selecionado diretamente de uma única coluna? Se você não sabe de onde vem, pode ser uma concatenação de várias colunas, ou o resultado de alguma função, ou um valor situado em um objeto de tabela aninhado. Portanto, você pode estar em uma perseguição de ganso selvagem tentando verificar cada coluna para esse valor. Você não pode começar com qualquer aplicativo cliente que esteja exibindo esse valor e tentar descobrir qual consulta ele está usando para obtê-lo?

De qualquer forma, a resposta de diciu fornece um método de geração de consultas SQL para verificar o valor em cada coluna de cada tabela. Você também pode fazer coisas semelhantes inteiramente em uma sessão SQL usando um bloco PL / SQL e SQL dinâmico. Aqui está um código escrito às pressas para isso:

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

Existem algumas maneiras de torná-lo mais eficiente também.

Nesse caso, dado o valor que você está procurando, você pode eliminar claramente qualquer coluna do tipo NÚMERO ou DATA, o que reduziria o número de consultas. Talvez até restrinja a colunas onde o tipo é como '% CHAR%'.

Em vez de uma consulta por coluna, você pode criar uma consulta por tabela assim:

SELECT * FROM table1
  WHERE column1 = 'value'
     OR column2 = 'value'
     OR column3 = 'value'
     ...
     ;
Dave Costa
fonte
Você deve restringi-lo às colunas char, varchar e varchar2, uma vez que as colunas de número e data não podem conter essa string.
Erich Kitzmueller
8
@ammoQ - como eu disse no penúltimo parágrafo?
Dave Costa
Eu executei isso no 9i e recebo um erro desconhecido column_name. Alguém pode me dizer qual modificação será necessária para executar isso no 9i?
Regmi de
@Regmi - desculpe, isso foi realmente um erro no meu código, não um problema de versão. O loop deveria ter sido conduzido por all_tab_columnsnão all_tables. Eu consertei.
Dave Costa
@DaveCosta - Obrigado pela correção, mas ainda recebo o erro 'table or view does not exist' na linha 6. Linha 6 sendo "Execute Immediate".
Regmi
34

Fiz algumas modificações no código acima para torná-lo mais rápido se você estiver pesquisando em apenas um proprietário. Você apenas tem que alterar as 3 variáveis ​​v_owner, v_data_type e v_search_string para ajustar o que você está procurando.

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
fonte
Tive que comentar a primeira linha para poder executar esta consulta. Além disso, não consegui remover o filtro do proprietário e executar a consulta.
Popa Andrei
1
Eu precisava colocar aspas duplas no nome da tabela / nome da coluna para evitar problemas quando eles precisassem ser citados:'SELECT COUNT(*) FROM "'||t.table_name||'" WHERE "'||t.column_name||'" = :1'
Steve Chambers
Observe que all_tab_cols também contém visualizações, apesar do nome
phil_w
o que exatamente é dbms_output? Porque as consultas são executadas com sucesso no DataGrip, mas não vejo nenhum resultado depois.
misteeque
Eu sei que isso é um pouco antigo, mas quando executo isso, obtenho apenas uma Saída de script de "bloqueio anônimo concluído"
JasonWH
7

Aqui está outra versão modificada que irá comparar uma correspondência de substring inferior. Isso funciona no 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
fonte
7

Sim, você pode e seu DBA irá odiá-lo e descobrirá que você tem que pregar os sapatos no chão, porque isso causará muito I / O e reduzirá o desempenho do banco de dados à medida que o cache for eliminado.

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

para começar.

Eu começaria com as consultas em execução, usando o v$sessione o v$sqlarea. Isso muda com base na versão do oracle. Isso reduzirá o espaço e não atingirá tudo.

Jim
fonte
7

Eu sei que este é um assunto antigo. Mas vejo um comentário sobre a questão perguntando se isso poderia ser feito em SQLvez de usarPL/SQL . Então pensei em postar uma solução.

A demonstração abaixo é buscar um VALOR em todas as COLUNAS de todas as TABELAS em um ESQUEMA inteiro :

  • Pesquise um tipo de CHARACTER

Vamos procurar o valor KINGno 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>
  • Pesquise um tipo NUMERIC

Vamos procurar o valor 20no 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
fonte
6
hrmm .... usar xml parece um exagero. Além disso:Error occurred in XML processing ORA-00932: inconsistent datatypes: expected NUMBER got BLOB
até
2
ORA-19202: Ocorreu um erro no processamento de XML ORA-00932: tipos de dados inconsistentes: esperado CHAR obteve BLOB ORA-06512: em "SYS.DBMS_XMLGEN", linha 288 ORA-06512: na linha 1 19202. 00000 - "Ocorreu um erro no processamento de XML % s "* Causa: Ocorreu um erro ao processar a função XML * Ação: Verifique a mensagem de erro fornecida e corrija o problema apropriado
Mohammad Faisal
Alguma ideia? ORA-19202: Ocorreu erro no processamento de XML ORA-22813: valor do operando excede os limites do sistema ORA-06512: em "SYS.DBMS_XMLGEN", linha 288 ORA-06512: na linha 1
Menios
5

Eu faria algo assim (gera todos os selects que você precisa). Mais tarde, você pode alimentá-los no 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;

Produz:

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

E o que ele faz é - para cada table_namede user_tablesobter cada campo (de desc) e criar um SELECT * da tabela onde o campo é igual a 'val'.

diciu
fonte
5

Modifiquei o script do Flood para ser executado uma vez para cada tabela em vez de para cada coluna de cada tabela para execução mais rápida. Requer Oracle 11g ou 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
fonte
5

Eu estava tendo os seguintes problemas para a resposta 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

A solução é:

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
fonte
4

se soubermos os nomes da tabela e da coluna, mas quisermos descobrir o número de vezes que a string está aparecendo 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
fonte
2

Procedimento para pesquisar todo o banco de dados:

    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;    

Executar declaração

  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 da amostra

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
fonte
1

Não tenho uma solução simples no promprt SQL. No entanto, existem algumas ferramentas como o sapo e o PL / SQL Developer que possuem uma GUI onde um usuário pode inserir a string a ser pesquisada e retornará a tabela / procedimento / objeto onde ela foi encontrada.

Dheer
fonte
1

Existem algumas ferramentas gratuitas que fazem esse tipo de pesquisa, por exemplo, esta funciona bem e o código-fonte está disponível: https://sites.google.com/site/freejansoft/dbsearch

Você precisará do driver ODBC Oracle e de um DSN para usar esta ferramenta.

John
fonte
1

Modificando o código para pesquisar sem distinção entre maiúsculas e minúsculas usando uma consulta LIKE em vez de encontrar correspondências exatas ...

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
fonte
0

- execução concluída - sem erro

    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
fonte
0

Pegar emprestado, melhorar um pouco e simplificar a partir desta postagem do blog a seguinte instrução SQL simples parece fazer o trabalho muito bem:

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 Chambers
fonte
-1
SELECT * from all_objects WHERE object_name like '%your_string%';
Rama Krishna
fonte