Search all varchar columns in table and output the row - oracle

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;

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

Related

Use wildcard to do fuzzy-search, with a variable

I am new to Oracle programming (started coding a month ago).
I am doing a fuzzy-search, as follows:
WHERE SQL_text like '%VARIABLE%'
The problem is, VARIABLE is a cursor that iterates through a table that looks like this:
USA
UK
Japan
...
Could you please advise how to specify a variable in that WHERE clause?
I've tried the following, but it doesn't work:
WHERE SQL_text like '%'||VARIABLE||'%'
Thank you very much! I greatly appreciate everyone's input!
It works, if you use it correctly. I don't have your tables, but - I have Scott's emp so I'll search for jobs that contain certain substring.
Table contents:
SQL> select ename, job from emp order by job;
ENAME JOB
---------- ---------
SCOTT ANALYST --> 2 analysts
FORD ANALYST
MILLER CLERK --> 4 clerks
JAMES CLERK
SMITH CLERK
ADAMS CLERK
BLAKE MANAGER
JONES MANAGER
CLARK MANAGER
KING PRESIDENT
TURNER SALESMAN
MARTIN SALESMAN
WARD SALESMAN
ALLEN SALESMAN
14 rows selected.
Code you're looking for:
SQL> set serveroutput on
SQL> declare
2 l_cnt number;
3 begin
4 for cur_r in (select 'ERK' var from dual union all --> clerks
5 select 'NALY' from dual --> analysts
6 )
7 loop
8 select count(*)
9 into l_cnt
10 from emp
11 where job like '%' || cur_r.var || '%';
12 dbms_output.put_line(cur_r.var || ' is contained in ' || l_cnt || ' row(s)');
13 end loop;
14 end;
15 /
ERK is contained in 4 row(s)
NALY is contained in 2 row(s)
PL/SQL procedure successfully completed.
SQL>

How to pass multiple parameters in oracle stored procedure

i am trying to execute like exec print_emp(1010,1111) but it will showing error.
create or replace
procedure print_emp(
P_empno NUMBER
)
IS
begin
for c in ( SELECT *
from emp
where empno in p_empno)
loop
dbms_output.put_line( c.empno||' '||c.ename||' '||c.job||' '||c.sal);
end loop;
END;
You'll need to create a type that is a collection of numbers, and then a procedure that accepts that collection.
Rather than use the IN operator, you should use MEMBER OF to test whether a scalar value is in a collection.
create or replace type tab_number is table of number
/
create or replace procedure print_nums
(p_nums in tab_number)
is
cursor c_main is
select column_value
from table(p_nums)
order by 1;
begin
for r_main in c_main loop
dbms_output.put_line(r_main.column_value);
end loop;
--
if 33 member of p_nums then
dbms_output.put_line('In the list');
end if;
end;
/
exec print_nums(tab_number(10,20,50));
exec print_nums(tab_number(10,20,33));
Obviously, if all parameters you'd like to pass represent the same column value, you can't just list them as if they were two different parameters. If that was the case, procedure should actually name them all, e.g.
create procedure print_emp(par_emp_1 in number, par_emp_2 in number)
but - what if there are 3 or 4 EMPNOs you'd like to pass? You can't modify the procedure every time (not just while declaring it, but also in SELECT statements, as you'd have to add new parameters there as well).
Therefore, one option might be this (as far as I understood the question):
one parameter, whose datatype is varchar2
it means that you'd have to enclose list of EMPNOs into single quotes and separate them with the same separator every time; let it be a comma , sign
cursor's query would contain a subquery (lines #6 - 9) which splits that comma-separated values list of EMPNO values into rows
the rest is simple
Here you go:
SQL> create or replace procedure print_emp(par_empno in varchar2)
2 is
3 begin
4 for c in (select *
5 from emp
6 where empno in (select to_number(regexp_substr(par_empno, '[^,]+', 1, level))
7 from dual
8 connect by level <= regexp_count(par_empno, ',') + 1
9 )
10 )
11 loop
12 dbms_output.put_line(c.empno||' '||c.ename||' '||c.job||' '||c.sal);
13 end loop;
14 end;
15 /
Procedure created.
Scott's EMP sample table:
SQL> select * from emp where deptno = 20;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17.12.1980 00:00:00 1000 20
7566 JONES MANAGER 7839 02.04.1981 00:00:00 2975 20
7788 SCOTT ANALYST 7566 09.12.1982 00:00:00 3000 20
7876 ADAMS CLERK 7788 12.01.1983 00:00:00 1100 20
7902 FORD ANALYST 7566 03.12.1981 00:00:00 3000 20
Testing:
SQL> set serveroutput on
SQL> exec print_emp('7369,7566,7788');
7566 JONES MANAGER 2975
7788 SCOTT ANALYST 3000
7369 SMITH CLERK 1000
PL/SQL procedure successfully completed.
SQL>

Creating dynamic oracle sql query

I need to dynamically create a query in Oracle's PLSQL.
What i mean is something like this:
declare
secondPart varchar2(100);
begin
select COLUMN into secondPart from TABLE where columnName='someName';
update firstPart_secondPart set SOME_COLUMN=1;
end
So basically what i want to do is to combine some constant string(firstPart_) with the dynamic value
You can use the execute immediate as follows:
declare
secondPart varchar2(100);
begin
select COLUMN into secondPart from TABLE where columnName='someName';
execute immediate 'update firstPart_' ||secondPart || ' set SOME_COLUMN=1';
--commit/rollback;
end;
/
For example:
SQL> create table test as
2 select 'empno' column_name, 'p' second_part from dual union all
3 select 'deptno' , 'pt' from dual;
Table created.
SQL> set serveroutput on
SQL> declare
2 first_part varchar2(20) := 'em';
3 l_str varchar2(200);
4 begin
5 select 'update ' || first_part || t.second_part ||
6 ' set comm = -100 where comm is null'
7 into l_str
8 from test t
9 where t.column_name = 'empno';
10
11 dbms_output.put_line(l_str);
12 execute immediate l_str;
13 end;
14 /
update emp set comm = -100 where comm is null
PL/SQL procedure successfully completed.
Result:
SQL> select empno, ename, comm from emp;
EMPNO ENAME COMM
---------- ---------- ----------
7369 SMITH -100
7499 ALLEN 300
7521 WARD 500
7566 JONES -100
7654 MARTIN 1400
7698 BLAKE -100
7782 CLARK -100
7788 SCOTT -100
7839 KING -100
7844 TURNER 0
7876 ADAMS -100
7900 JAMES -100
7902 FORD -100
7934 MILLER -100
14 rows selected.
SQL>

how to get this order by working in oracle pl/sql

ORDER BY CASE
WHEN v_SORT_TYPE = 'ASC' THEN
CASE
WHEN v_SORT_ORDER = 'lname' THEN CAST(lname AS VARCHAR2(45)) || ',' || ROWNUM
WHEN v_SORT_ORDER = 'code' THEN CAST(code AS VARCHAR2(52)) || ',' || ROWNUM
END ASC
WHEN v_SORT_TYPE = 'DSC' THEN
CASE
WHEN v_SORT_ORDER = 'lname' THEN CAST(lname AS VARCHAR2(45)) || ',' || ROWNUM
WHEN v_SORT_ORDER = 'code' THEN CAST(code AS VARCHAR2(52)) || ',' || ROWNUM
END DESC
END
I am trying to write conditions for when v_SORT_TYPE is passed in as ASC or DSC. I am getting compilation error in this.
I assume this would be a common thing to do in my pl/sql SP's.
where am I going wrong?
You need to rethink the order by into multiple "columns".
ORDER BY
CASE WHEN v_sort_type = 'ASC' AND v_sort_order = 'lname' THEN lname END ASC,
CASE WHEN v_sort_type = 'DESC' AND v_sort_order = 'lname' THEN lname END DESC,
CASE WHEN v_sort_type = 'ASC' AND v_sort_order = 'code' THEN cname END ASC,
CASE WHEN v_sort_type = 'DESC' AND v_sort_order = 'code' THEN cname END DESC
Only one of those would be in effect at any one time, so you'd effectively get (for example)
ORDER BY null ASC,null DESC,code ASC, null DESC
or
ORDER BY null ASC,lname DESC,null ASC, null DESC
The alternative to a cut'n'pasted CASE() statement is to shape the query in-flight with dynamic SQL.
Here is the simplest demo possible.
create or replace function get_emps
( p_dno in emp.deptno%type
, p_sort_col in varchar2 := 'EMPNO'
, p_asc_desc in varchar2 := 'ASC')
return sys_refcursor
is
stmt varchar2(32767);
return_value sys_refcursor;
begin
stmt := 'select * from emp where deptno = :1';
if p_sort_col is not null
then
stmt := stmt ||' order by '||p_sort_col||' '||p_asc_desc;
end if;
open return_value for stmt using p_dno;
return return_value;
end get_emps;
/
And this is it in action ...
SQL> var rc refcursor
SQL> exec :rc := get_emps(20, 'ENAME')
PL/SQL procedure successfully completed.
SQL> print rc
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 CLARKE CLERK 7902 17-DEC-80 800 20
7902 GASPAROTTO ANALYST 7566 03-DEC-81 3000 20
7876 KULASH CLERK 7788 23-MAY-87 1100 20
7788 RIGBY ANALYST 7566 19-APR-87 3000 20
7566 ROBERTSON MANAGER 7839 02-APR-81 2975 20
SQL> exec :rc := get_emps(20, 'SAL', 'DESC')
PL/SQL procedure successfully completed.
SQL> print rc
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7788 RIGBY ANALYST 7566 19-APR-87 3000 20
7902 GASPAROTTO ANALYST 7566 03-DEC-81 3000 20
7566 ROBERTSON MANAGER 7839 02-APR-81 2975 20
7876 KULASH CLERK 7788 23-MAY-87 1100 20
7369 CLARKE CLERK 7902 17-DEC-80 800 20
SQL>
ASC and DESC are keywords. CASE statements are going to evaluate to a value, not a keyword. One option would be dynamic SQL (build a string and then execute), another option would be to have an IF clause at a higher level. If you were only sorting by numbers, you could multiply by negative 1, but strings are tougher. I guess you could do something crazy like having a second table that stores strings and sort order as a number, and multiplying that number by -1, but I have a hard time believing that would be better in any conceivable way than the first two options.

What is the equivalent of SQL Server APPLY in Oracle?

I am new to Oracle. Is there a builtin keyword does the same job of SQL Server APPLY?
I think the equivalent of the APPLY clause in Oracle is called a lateral JOIN. A lateral join in Oracle is when you join a table A with a function F that outputs rows and this function has columns of A as parameters.
Let's build a small example with this setup:
SQL> CREATE OR REPLACE TYPE emp_row AS OBJECT (
2 empno NUMBER(4),
3 ename VARCHAR(10),
4 job VARCHAR(9),
5 deptno NUMBER(2)
6 );
7 /
Type created
SQL> CREATE OR REPLACE TYPE emp_tab AS TABLE OF emp_row;
2 /
Type created
SQL> CREATE OR REPLACE FUNCTION get_emp_dept(p_deptno NUMBER) RETURN emp_tab IS
2 l_result emp_tab;
3 BEGIN
4 SELECT emp_row(empno, ename, job, deptno)
5 BULK COLLECT INTO l_result
6 FROM emp
7 WHERE deptno = p_deptno;
8 RETURN l_result;
9 END get_emp_dept;
10 /
Function created
A lateral join is automatic in Oracle, there is no special keyword:
SQL> SELECT dept.dname, emp.empno, emp.ename, emp.job
2 FROM dept
3 CROSS JOIN TABLE(get_emp_dept(dept.deptno)) emp;
DNAME EMPNO ENAME JOB
-------------- ----- ---------- ---------
ACCOUNTING 7782 CLARK MANAGER
ACCOUNTING 7839 KING PRESIDENT
ACCOUNTING 7934 MILLER CLERK
RESEARCH 7369 SMITH CLERK
RESEARCH 7566 JONES MANAGER
RESEARCH 7788 SCOTT ANALYST
RESEARCH 7876 ADAMS CLERK
RESEARCH 7902 FORD ANALYST
SALES 7499 ALLEN SALESMAN
SALES 7521 WARD SALESMAN
SALES 7654 MARTIN SALESMAN
SALES 7698 BLAKE MANAGER
SALES 7844 TURNER SALESMAN
SALES 7900 JAMES CLERK
14 rows selected
In Oracle we can use a pipelined function in the FROM clause by using the TABLE() function.
SQL> select * from table( get_dept_emps (10) )
2 /
ENAME SAL MGR
------------------------------ ---------- ---------------------
BOEHMER 2450 SCHNEIDER
SCHNEIDER 5000
KISHORE 1300 BOEHMER
SQL>
This can be treated like any other table, for instance, by joining to it:
SQL> select t.*
2 , e.empno
3 from
4 table( get_dept_emps (10) ) t
5 join emp e
6 on e.ename = t.ename
7 /
ENAME SAL MGR EMPNO
---------- ---------- ---------- ----------
BOEHMER 2450 SCHNEIDER 7782
SCHNEIDER 5000 7839
KISHORE 1300 BOEHMER 7934
SQL>
Since 12c, Oracle supports both APPLY and LATERAL natively: https://docs.oracle.com/database/121/NEWFT/chapter12101.htm#FEATURENO10330

Resources