代码之家  ›  专栏  ›  技术社区  ›  XantorAmnobius

在oracle数据库中动态搜索2个值

  •  1
  • XantorAmnobius  · 技术社区  · 7 年前

    大家好,

    试着把我的头包起来,设法让它在某种程度上发挥作用-但仍有点挣扎。

    对于本例,我想在两列中找到两个值: VARCHAR和日期 约翰1984-01-01

    假设这是约翰的生日。

    我希望能够在let-say 2000表中找到带有日期值的值john,并使列名不尽相同的事情复杂化。数据类型是。

    下面选择我需要的具有两个列名的所有表。

    SELECT A.TABLE_NAME, A.COLUMN_NAME, B.COLUMN_NAME
      FROM all_tab_columns A
           JOIN all_tab_columns B
              ON     A.TABLE_NAME = B.TABLE_NAME
                 AND B.DATA_TYPE = 'DATE'
                 AND A.COLUMN_NAME IN ('NAME', 'FULLNAME')
    

    所以,现在我得到我需要的表,其中有数据类型日期,其中列名name和全名存在。

    所以现在在2000张表中,我有300张符合我的标准。我想搜索找到的日期为1984-01-01的john表,其中john可以是全名或名称,1984-01-01可以是任何列名,即dttm、bday、datefld、dtfield等。

    我想要这两个值存在于同一行的表名,而不是其他值。

    我已经看过这些例子:

    Search All Fields In All Tables For A Specific Value (Oracle)

    但还是有问题。我错过了什么?

    DECLARE
       match_count       INTEGER;
       v_search_string   VARCHAR2 (11) := 'JOHN';
    BEGIN
       FOR t
          IN (SELECT A.owner, A.table_name, A.column_name
                FROM all_tab_columns A
                     JOIN all_tab_columns B
                        ON     A.TABLE_NAME = B.TABLE_NAME
                           AND A.COLUMN_NAME IN ('NAME', 'FULLNAME')
                           AND B.DATA_TYPE = 'DATE'
                           AND A.TABLE_NAME LIKE 'DATA%')
       LOOP
          BEGIN
             EXECUTE IMMEDIATE
                   'SELECT * FROM '
                || t.owner
                || '.'
                || 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
                   );
             END IF;
          EXCEPTION
             WHEN OTHERS
             THEN
                DBMS_OUTPUT.put_line (
                      'Error encountered trying to read '
                   || t.column_name
                   || ' from '
                   || t.owner
                   || '.'
                   || t.table_name);
          END;
       END LOOP;
    END;
    /
    
    2 回复  |  直到 7 年前
        1
  •  1
  •   Maxim Borunov    7 年前

    我对你的代码做了一些小修改:

    DECLARE
       match_count       INTEGER;
       v_search_string   VARCHAR2 (11) := 'JOHN';
       v_search_date      DATE          := date '1984-01-01';
    BEGIN
       FOR t IN (
         SELECT A.owner, A.table_name, A.column_name text_column_name, B.column_name date_column_name
         FROM   all_tab_columns A
                JOIN all_tab_columns B
                   ON  A.TABLE_NAME = B.TABLE_NAME
                   AND A.OWNER = B.OWNER
                   AND A.COLUMN_NAME IN ('NAME', 'FULLNAME')
                   AND B.DATA_TYPE = 'DATE'
                   AND A.TABLE_NAME LIKE 'DATA%'
       ) LOOP
          BEGIN
             EXECUTE IMMEDIATE
                   'SELECT count(*) FROM '
                || t.owner
                || '.'
                || t.table_name
                || ' WHERE '
                || t.text_column_name
                || ' = :1'
                || ' and '
                || t.date_column_name
                || ' = :2'
                INTO match_count
                USING v_search_string, v_search_date;
    
             IF match_count > 0
             THEN
                DBMS_OUTPUT.put_line (
                    'Found! '||t.table_name
                   );
             ELSE
                DBMS_OUTPUT.put_line (
                    'No matches for '||t.table_name||'('||t.text_column_name||','||t.date_column_name||')'
                   );
             END IF;
          EXCEPTION
             WHEN OTHERS
             THEN
                DBMS_OUTPUT.put_line (
                      'Error encountered trying to read from '
                   || t.owner
                   || '.'
                   || t.table_name);
          END;
       END LOOP;
    END;
    /
    
        2
  •  0
  •   Kaushik Nayak    7 年前

    您可以尝试使用 all_tab_columns 并在动态where子句中查询相应的表。

    SET serveroutput ON 
    DECLARE 
        v_count INTEGER; 
    BEGIN 
        FOR r IN (SELECT a.table_name, 
                         ' WHERE ' 
                         || LISTAGG(CASE data_type 
                                      WHEN 'DATE' THEN column_name 
                                                       || ' = DATE ' 
                                                       || '''1984-01-01''' 
                                      ELSE column_name 
                                           || ' = ' 
                                           || '''JOHN''' 
                                    END, ' or ') 
                              WITHIN GROUP( ORDER BY column_name ) AS where_clause 
                  FROM   user_tab_columns a 
                  WHERE  column_name IN ( 'NAME', 'FULLNAME' ) 
                          OR ( data_type = 'DATE' 
                               AND column_name IN ( 'DTTM', 'BDAY', 'DATEFLD', 
                                                    'DTFIELD' 
                                                  ) ) 
                  GROUP  BY table_name) LOOP 
            EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM '|| r.table_name|| 
            r.where_clause 
            INTO 
            v_count; 
    
            IF v_count > 0 THEN 
              dbms_output.Put_line(r.table_name); 
            END IF; 
        END LOOP; 
    END; 
    
    / 
    

    注意,我给你的是一个例子,它给了你一些想法,你可以构造它并调整隐式游标循环的查询(在 r in () )为了满足你的需要 AND/OR/JOIN 条件 INTERSECT 不管你要什么接线员等。我这样做是为了从我这边测试。

    编辑 :另一个选项是强制方法,即使用动态where子句搜索数据库中的所有表,如果在表中找不到where子句中的列,则忽略异常。

    SET serveroutput ON 
    DECLARE 
        v_count        NUMBER; 
        v_where_clause VARCHAR2(400) := 
        ' WHERE DTTM = DATE ''1984-01-01'' AND FULLNAME = ''JOHN'''; 
    BEGIN 
        FOR r IN (SELECT owner, 
                         table_name 
                  FROM   all_tables) LOOP 
            BEGIN 
                EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM '|| r.owner|| '.'|| 
                r.table_name 
                || 
                v_where_clause INTO v_count; 
    
                IF v_count > 0 THEN 
                  dbms_output.put_line(r.owner||'.'||r.table_name); 
                END IF; 
            EXCEPTION 
                WHEN OTHERS THEN 
                  NULL; --It is ok in this case as you know why it fails. 
            END; 
        END LOOP; 
    END; 
    /