0

I need an assistance with regard to building a PL/SQL block related to the following query:

SELECT <PRIMARY_KEY_COLUMN>, <VARCHAR_COLUMN> FROM TABLENAME WHERE REGEXP_LIKE(VARCHAR_COLUMN, UNISTR('[\D800-\DFFF]'));

The above query will give an output related to all the UTF8 bytes that are mentioned in the range.

I would request you guys to help me with modifying the above query, so that I can run it on all the VARCHAR/CLOB columns in the table and get an output like this:

ColumnName                   Value                 Primary_key_Column
-----------------------------------------------------------------------
Col1                         v1                     123
Col1                         v2                     124
.
.
Col2                         v1                     167
Col2                         v2                     123
.
.

Kindly review and please share your comments.

UPDATE1:

I was able to build the following block from the comments I received and from one of the posts, but it still requires edits:

set serveroutput on;
DECLARE
  match_count integer;
  v_search_string varchar2(4000) := 'shazamTemplateId';
BEGIN  
  FOR t IN (SELECT owner, table_name, column_name FROM all_tab_columns WHERE data_type in ('CHAR', 'VARCHAR2', 'NCHAR', 'NVARCHAR2', 'CLOB', 'NCLOB') AND table_name = 'DECORATION_FIELDS') 
  LOOP   
    BEGIN
      EXECUTE IMMEDIATE    
          'SELECT COUNT(*) FROM ' || t.owner || '.' || t.table_name || ' WHERE REGEXP_LIKE( '||t.column_name||' = :1)'
          INTO match_count
          USING UNISTR('[\D800-\DFFF]');
      IF match_count > 0 THEN 
        dbms_output.put_line( t.owner || '.' || t.table_name ||' '||t.column_name||' '||match_count );
      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;
William Robertson
  • 15,273
  • 4
  • 38
  • 44
arsm4
  • 101
  • 2
  • 10
  • @KaushikNayak : I tried the queries, mentioned in the following posts: [link]https://stackoverflow.com/questions/208493/search-all-fields-in-all-tables-for-a-specific-value-oracle [link]https://stackoverflow.com/questions/12921792/oracle-database-search-all-tables-for-string-return-row-data/12923379 But faced a roadblock with regards to the pattern based searching that I am doing. Hence, posted the question – arsm4 Oct 09 '17 at 03:49
  • @JSapkota : So with regards to the `select` query that I have mentioned in my question, it outputs the **primary_key_column_value,varchar_column** which matches the pattern that I have provided. So what I am trying to build here is a query which will scan all the varchars/clob columns in a particular table and give me the output as I have mentioned. Kindly help. – arsm4 Oct 09 '17 at 03:54
  • just replace ` ' WHERE '||t.column_name||' = :1'....USING` with `WHERE REGEXP_LIKE( '||t.column_name||' ,:1')` and `USING UNISTR('[\D800-\DFFF]')` – Kaushik Nayak Oct 09 '17 at 03:54
  • @KaushikNayak But that is giving me the **count**. I am sorry for being dumb, but can you please tell me how can I modify the select query to get the output format that I need? – arsm4 Oct 09 '17 at 04:01
  • You need to select the primary key column and matched column name in select instead of count. Then display it in the order you like. – Kaushik Nayak Oct 09 '17 at 04:22
  • I'LL typing on my mobile now so won't be able to add much code.Lets wait for somebody to answer. – Kaushik Nayak Oct 09 '17 at 04:27
  • @KaushikNayak I am really not sure, how I need to modify this **'SELECT COUNT(*) FROM ' || t.owner || '.' || t.table_name || ' WHERE REGEXP_LIKE( '||t.column_name||' = :1)' INTO match_count USING UNISTR('[\D800-\DFFF]');** as per my requirement. Since my output will be more than 1 row. Kindly advice. – arsm4 Oct 09 '17 at 04:35
  • 1. You can't do that in a single table output (if you want to do it both for CLOB and for VARCHAR columns). (By the way, why VARCHAR and not VARCHAR2? And then, why not also CHAR? And perhaps NVARCHAR2 and NCHAR, too?) Why can't you have everything in a single output? Look at your second column, containing the values. What data type will it be? Obviously, it must accommodate both CLOB values and VARCHAR2 values. How do you plan to do that? –  Oct 09 '17 at 04:38
  • Then - are you asking how to do this WITHOUT knowing the column names ahead of time? Or do you have the list, you just need to adapt the query? If you want a "generic" query that will read the column names and data types from a catalog view, it can be done (with "dynamic SQL"); however, in most cases if you need to use dynamic SQL, that is a sign that you are asking the wrong question. –  Oct 09 '17 at 04:39
  • @mathguy I have added a code block with regards that I built using the posts that I have mentioned. Yes, I am trying to include **CHAR** and **CLOB** related columns, since this range of **UTF8** bytes are breaking our migration. Hence, the reason I am trying to build the above query, where atleast if I can get the **Column_names, associated_values** without the primary key, it will be enough. If Dynamic SQL is the answer, then kindly help me. – arsm4 Oct 09 '17 at 04:49
  • @JSapkota, yes. You can't directly use the select query if you need all values. Also, if your primary key column is not fixed you may may have to use the query given here https://stackoverflow.com/questions/9016578/how-to-get-primary-key-column-in-oracle. Then loop through the records of the table using another loop – Kaushik Nayak Oct 09 '17 at 04:53
  • I understand that you want to search columns of different data types. What I am saying is that you can't show values of different data types in the same column in the output (not unless you convert them all to CLOB, which seems unhealthy). Write separate queries for the VARCHAR2 columns and for the CLOB columns. Writing only one should suffice, the others should be very simple modifications of the first one. –  Oct 09 '17 at 04:57
  • By the way, I would write `data_type like '%CHAR%'` instead of painstakingly listing every individual string type. – William Robertson Oct 09 '17 at 06:58

1 Answers1

0

Here is a static solution (it does not require any PL/SQL code, but it require prior knowledge of the table and column names, and knowing which columns must be included). It also assumes all the "text" columns are VARCHAR2; as I explained in a Comment, you shouldn't expect to be able to return VARCHAR2 and CLOB values in the same column in the output. (Perhaps, if you must do everything in one go, you need several columns in the output: column_name but also column_type, as in VARCHAR2 vs CLOB, and then two value columns, one for VARCHAR2 columns in the original table and the other one for CLOB values.)

You can use something similar with PL/SQL code to make it dynamic; I don't recommend it.

So, anyway, here is the static solution. It uses the EMP table in the SCOTT schema. The PK is EMPNO (NUMBER data type), there are two VARCHAR2 columns, ENAME and JOB. EMP looks like this:

select * from emp;

EMPNO ENAME      JOB         MGR HIREDATE              SAL  COMM     DEPTNO
----- ---------- --------- ----- ------------------- ----- ----- ----------
 7369 SMITH      CLERK      7902 1980-12-17 00:00:00   800               20
 7499 ALLEN      SALESMAN   7698 1981-02-20 00:00:00  1600   300         30
 7521 WARD       SALESMAN   7698 1981-02-22 00:00:00  1250   500         30
 7566 JONES      MANAGER    7839 1981-04-02 00:00:00  2975               20
 7654 MARTIN     SALESMAN   7698 1981-09-28 00:00:00  1250  1400         30
 7698 BLAKE      MANAGER    7839 1981-05-01 00:00:00  2850               30
 7782 CLARK      MANAGER    7839 1981-06-09 00:00:00  2450               10
 7788 SCOTT      ANALYST    7566 1987-04-19 00:00:00  3000               20
 7839 KING       PRESIDENT       1981-11-17 00:00:00  5000               10
 7844 TURNER     SALESMAN   7698 1981-09-08 00:00:00  1500     0         30
 7876 ADAMS      CLERK      7788 1987-05-23 00:00:00  1100               20
 7900 JAMES      CLERK      7698 1981-12-03 00:00:00   950               30
 7902 FORD       ANALYST    7566 1981-12-03 00:00:00  3000               20
 7934 MILLER     CLERK      7782 1982-01-23 00:00:00  1300               10

The query to solve your problem: (the query searches for values that contain the characters from V to Z; adapt to your needs)

select   col_name, val, empno
from     emp
unpivot  ( val for col_name in (ename as 'ENAME', job as 'JOB') )
where    regexp_like( val, '[V-Z]' )
order by col_name, empno -- If needed
;

COL_NAME VAL        EMPNO
-------- ---------- -----
ENAME    WARD        7521
JOB      ANALYST     7788
JOB      ANALYST     7902
  • That really helps. Can you please help in transforming this `SELECT REGEXP_SUBSTR(str, UNISTR('[\FFFF-\DBFF\DFFF]')) AS substr, primary_key FROM (SELECT VARCHAR_COLUMN str, PRIMARY_COLUMN primary_key FROM CS_ACTIONS) where REGEXP_SUBSTR(str, UNISTR('[\FFFF-\DBFF\DFFF]')) is not null;` to the final select query that you have mentioned? Thnx a ton again!! – arsm4 Oct 09 '17 at 05:46