I want to implement one search logic here. What I want is,
A user enters any text in the search box and presses enter. Then what should happen is, it should search in the table on any column and it the record exist then it should display.
Currently what I tried is, it search from one of the column from the table. Below is the code,
PROCEDURE GET_SEARCH_DATA
(
P_INPUTTEXT IN NVARCHAR2,
P_RETURN OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN P_RETURN FOR
SELECT APP_MST_ID, APPLICATIONNAME, PROJECTNO, VSS_FOLDER_LOC FROM
APPLICATION_MASTER WHERE APPLICATIONNAME LIKE '%'|| P_INPUTTEXT || '%';
END;
So what I want is, it should search from every column of the table and display the result.
This is a rather rudimentary "solution" which checks all tables and their columns (from USER_TAB_COLUMNS) and checks which ones of them contain a search string; it displays a table, a column and number of occurrences.
SQL> declare
2 l_str varchar2 (500);
3 l_cnt number := 0;
4 begin
5 for cur_r in (select u.table_name, u.column_name
6 from user_tab_columns u)
7 loop
8 l_str :=
9 'SELECT COUNT(*) FROM '
10 || cur_r.table_name
11 || ' WHERE '
12 || upper (cur_r.column_name)
13 || ' like (''%&search_string%'')';
14
15 execute immediate (l_str) into l_cnt;
16
17 if l_cnt > 0
18 then
19 dbms_output.put_line (
20 cur_r.table_name || '.' || cur_r.column_name || ': ' || l_cnt);
21 end if;
22 end loop;
23 end;
24 /
Enter value for search_string: MANAGE
EMP.JOB: 3
PL/SQL procedure successfully completed.
SQL> /
Enter value for search_string: ACCOU
DEPT.DNAME: 1
PL/SQL procedure successfully completed.
SQL>
Related
STEP 1:
Creating a new table and adding a record to the database with the help of the procedure
• the function will take 3 parameters string ,string and number,
• The first parameter represents the name of the table, and the other two parameters represent the records to be added to the created table.
Step 2:
As seen in the screenshot below; As described above, when the function runs, a table named 'mytable' will be created in the database with the first parameter, and other parameters will be added to the created table as a record.
enter image description here
Why function? They are supposed to return some value, not to perform DDL. Therefore, a procedure it is, using dynamic SQL as otherwise it won't work.
SQL> CREATE OR REPLACE PROCEDURE p_test (par_table_name IN VARCHAR2,
2 par_str IN VARCHAR2,
3 par_num IN NUMBER)
4 IS
5 l_cnt NUMBER;
6 BEGIN
7 SELECT COUNT (*)
8 INTO l_cnt
9 FROM user_tables
10 WHERE UPPER (table_name) = UPPER (par_table_name);
11
12 IF l_cnt = 0
13 THEN
14 EXECUTE IMMEDIATE 'create table '
15 || par_table_name
16 || ' as select '
17 || DBMS_ASSERT.enquote_literal (par_str)
18 || ' col_string, '
19 || par_num
20 || ' col_number from dual';
21 ELSE
22 raise_application_error (-20000,
23 'Object with that name already exists');
24 END IF;
25 END;
26 /
Procedure created.
Testing:
SQL> exec p_test('little', 'foot', 20);
PL/SQL procedure successfully completed.
SQL> select * from little;
COL_ COL_NUMBER
---- ----------
foot 20
SQL>
how to write a oracle or PLSQL script to find the column names which is 'number' datatype in table and find the average and median of that column name in table.
Well, yes, if you're just beginning your PL/SQL adventure, your homework isn't as easy as it sounds. It requires you to know
where to search for such a column (in data dictionary, user_tab_columns contains that info)
how to check ALL of them (in a loop)
how to calculate average value (using appropriate function, avg)
how to actually do that (using dynamic SQL)
how to display the result (that's the simplest part, if dbms_output.put_line satisfies your needs)
that doesn't always work, which puzzles quite a few people - you have to enable output (using set serveroutput on in SQL*Plus or SQL Developer; enable it by clicking appropriate button in your GUI tool)
The following code shows the way. Feel free to improve it.
SQL> set serveroutput on;
SQL> declare
2 l_table_name varchar2(30) := 'EMP';
3 l_str varchar2(200);
4 l_avg number;
5 begin
6 for cur_r in (select column_name
7 from user_tab_columns
8 where table_name = l_table_name
9 and data_type = 'NUMBER'
10 )
11 loop
12 l_str := 'select avg(' || cur_r.column_name ||')' ||
13 ' from ' || l_table_name;
14 execute immediate l_str into l_avg;
15
16 dbms_output.put_line
17 ('Average of ' || cur_r.column_name || ' = ' || l_avg);
18 end loop;
19 end;
20 /
Average of EMPNO = 7726,571428571428571428571428571428571429
Average of MGR = 7739,307692307692307692307692307692307692
Average of SAL = 2073,214285714285714285714285714285714286
Average of COMM = 550
Average of DEPTNO = 22,14285714285714285714285714285714285714
PL/SQL procedure successfully completed.
SQL>
As my permissions are limited, the following can not be created as procedure.
I need help on developing a Dynamic SQL that checks a table for duplicate unique IDs.
Also, is it possible to have more than one table to be checked for duplicates with the same query?
declare
table_name is table:= table_1
unique_id varchar2(100):= unique_1
begin
select unique_id,
count(unique_id) as count_unique
from table_name
having count(unique_id)>1
group by unique_id
end;
/
If you can't create a stored procedure (or a function), you're doomed to an anonymous PL/SQL block. Here's one that works in SQL*Plus (probably in SQL Developer as well). Read comments within code.
For Scott's EMP table, number of jobs is
SQL> select job, count(*) from emp group by job;
JOB COUNT(*)
--------- ----------
CLERK 4
SALESMAN 4
PRESIDENT 1
MANAGER 3
ANALYST 2
SQL>
You'd then
SQL> declare
2 l_table varchar2(30) := '&PAR_TABLE_NAME';
3 l_column varchar2(30) := '&PAR_COLUMN_NAME';
4 l_str varchar2(500);
5 l_rc sys_refcursor;
6 --
7 l_ret_column varchar2(30);
8 l_ret_cnt number;
9 begin
10 -- compose a SELECT statement
11 l_str := 'select ' || l_column || ', count(*) cnt ' ||
12 ' from ' || l_table ||
13 ' group by ' || l_column ||
14 ' having count(*) > 1';
15
16 -- use L_STR as a "source" for the L_RC (ref)cursor
17 open l_rc for l_str;
18
19 -- loop, fetch data, display what you've found
20 loop
21 fetch l_rc into l_ret_column, l_ret_cnt;
22 exit when l_rc%notfound;
23
24 dbms_output.put_line(l_table ||'.'|| l_column ||' = ' ||
25 l_ret_column ||', ' || l_ret_cnt || ' row(s)');
26 end loop;
27
28 close l_rc;
29 end;
30 /
Enter value for par_table_name: emp
Enter value for par_column_name: job
emp.job = CLERK, 4 row(s)
emp.job = SALESMAN, 4 row(s)
emp.job = MANAGER, 3 row(s)
emp.job = ANALYST, 2 row(s)
PL/SQL procedure successfully completed.
SQL>
Select all the tables of database where column match than pass table name to next query using loop. If column name and column values matches than return true and exist for loop using a stored procedure:
CREATE OR REPLACE PROCEDURE TEST
(
NAME IN VARCHAR2 ,
ID IN NUMBER,
RE OUT SYS_REFCURSOR
) AS
BEGIN
OPEN RE FOR SELECT A.TABLE_NAME FROM
user_tables A JOIN user_tab_columns C
ON C.TABLE_NAME = A.TABLE_NAME
WHERE C.COLUMN_NAME = NAME;
FOR RE IN LOOP
v_Sql := 'SELECT COUNT(*) FROM '|| LOOP.TABLE_NAME || 'WHERE COLUMN_NAME =
ID';
EXECUTE IMMEDIATE v_Sql
IF v_Sql%ROWCOUNT > 0 THEN
return true;
EXIT
END LOOP;
END TEST;
For more understanding the problem
//Get all the tables of database where campus_id is exist in any table of
database
Campus, Class, Section (3 tables found)
Apply forloop on the records
Select count(campus_id) as total from (table name using loop) where campus_id = 1(value
pass)
if(total > 0){
Exist for loop and return true
}
else{
Again iterate the loop to next value
}
What you described doesn't make much sense. If there are several tables that contain a column you're checking and you exit the loop as soon as you find the first one, what about the rest of them?
Here's what I'd do, see if it helps. I'll create a function (not a procedure) that returns a table. In order to do that, I'll create type(s) first:
SQL> create or replace type t_record as object (tn varchar2(30), cnt number);
2 /
Type created.
SQL> create or replace type t_table as table of t_record;
2 /
Type created.
SQL>
The function:
in a cursor FOR loop I'm selecting tables that contain that column
L_STR is used to compose the SELECT statement
DBMS_OUTPUT.PUT_LINE is used to display it first, so that I could visually check whether it is correctly set or not.
if it is, I'm running it with the EXECUTE IMMEDIATE
the result is stored into a table type and returned to the caller
SQL> create or replace function f_colname
2 (par_column_name in varchar2,
3 par_column_value in varchar2
4 )
5 return t_table
6 is
7 retval t_table := t_table();
8 l_str varchar2(200);
9 l_cnt number;
10 begin
11 for cur_r in (select table_name
12 from user_tab_columns
13 where column_name = par_column_name
14 )
15 loop
16 l_str := 'select count(*) from ' || cur_r.table_name ||
17 ' where ' || par_column_name || ' = ' ||
18 chr(39) || par_column_value || chr(39);
19 -- Display l_str first, to make sure that it is OK:
20 -- dbms_output.put_line(l_str);
21 execute immediate l_str into l_cnt;
22 retval.extend;
23 retval(retval.count) := t_record(cur_r.table_name, l_cnt);
24 end loop;
25 return retval;
26 end;
27 /
Function created.
Testing:
SQL> select * from table (f_colname('DEPTNO', '10'));
TN CNT
------------------------------ ----------
TEST_201812 1
DEPT 1
EMP 3
SQL> select * from table (f_colname('ENAME', 'KING'));
TN CNT
------------------------------ ----------
EMP 1
BONUS 1
SQL>
That won't work properly for some datatypes (such as DATE) and will have to be adjusted, if necessary.
[EDIT: after you edited the question]
OK then, that's even simpler. It should still be a function (that returns a Boolean, as you said that - in case that something's being found - you want to return TRUE). Code is pretty much similar to the previous function.
SQL> create or replace function f_colname
2 (par_column_name in varchar2,
3 par_column_value in varchar2
4 )
5 return boolean
6 is
7 l_str varchar2(200);
8 l_cnt number;
9 retval boolean := false;
10 begin
11 for cur_r in (select table_name
12 from user_tab_columns
13 where column_name = par_column_name
14 )
15 loop
16 l_str := 'select count(*) from ' || cur_r.table_name ||
17 ' where ' || par_column_name || ' = ' ||
18 chr(39) || par_column_value || chr(39);
19 -- Display l_str first, to make sure that it is OK:
20 -- dbms_output.put_line(l_str);
21 execute immediate l_str into l_cnt;
22 if l_cnt > 0 then
23 retval := true;
24 exit;
25 end if;
26 end loop;
27 return retval;
28 end;
29 /
Function created.
Testing: as you can't return Boolean at SQL layer, you have to use an anonymous PL/SQL block, as follows:
SQL> declare
2 l_ret boolean;
3 begin
4 if f_colname('DEPTNO', '15') then
5 dbms_output.put_line('It exists');
6 else
7 dbms_output.put_line('It does not exist');
8 end if;
9 end;
10 /
It does not exist
PL/SQL procedure successfully completed.
SQL>
I have a procedure which receive as input parameter a record with 170 columns (it is based on the structure of a table).
In the procedure I want to call a debugging procedure one of whose parameters is a text string containing all the field names and values of this record.
For example:
CREATE OR REPLACE PROCEDURE xxx (pi_record IN table_name%ROWTYPE) as
text VARCHAR2(10000) := NULL;
BEGIN
...
text := 'pi_record.column1 = ' || pi_record.column1 || CHR(13) ||
'pi_record.column2 = ' || pi_record.column2 || CHR(13) ||
...
'pi_record.column170 = ' || pi_record.column170;
logging_procedure (text);
...
END;
Is there any simple way to achieve this in a dynamic way (looping through record fields names and values) without enumerating all of them?
Maybe something like this:
CREATE OR REPLACE PROCEDURE xxx (pi_record IN table_name%ROWTYPE) as
text VARCHAR2(10000) := NULL;
BEGIN
...
LOOP in pi_record.columns
text := text || CHR(13) || pi_record.column.name || ' : ' || pi_record.column.value
END LOOP
logging_procedure (text);
...
END;
Many thanks,
Here's one way to do that. A package spec contains a variable whose type matches the one we'll use in a procedure.
SQL> set serveroutput on
SQL> create or replace package pkg_xxx
2 as
3 dept_rec dept%rowtype;
4 end;
5 /
Package created.
SQL> create or replace procedure xxx (pi_record in dept%rowtype)
2 as
3 text varchar2 (10000) := null;
4 l_str varchar2 (200);
5 l_var varchar2 (200);
6 begin
7 pkg_xxx.dept_rec := pi_record;
8
9 for cur_r in ( select column_name
10 from user_tab_columns
11 where table_name = 'DEPT'
12 order by column_id)
13 loop
14 l_str :=
15 'begin '
16 || ':x := to_char(pkg_xxx.dept_rec.'
17 || cur_r.column_name
18 || '); '
19 || 'end; ';
20
21 execute immediate l_str using out l_var;
22
23 text := text || chr (10) || cur_r.column_name || ' = ' || l_var;
24 end loop;
25
26 dbms_output.put_line (text);
27 end;
28 /
Procedure created.
Now, let's pass something to the procedure and see what happens:
SQL> declare
2 cursor c1
3 is
4 select *
5 from dept
6 where deptno = 10;
7
8 c1r c1%rowtype;
9 begin
10 open c1;
11 fetch c1 into c1r;
12 close c1;
13
14 xxx (c1r);
15 end;
16 /
DEPTNO = 10
DNAME = ACCOUNTING
LOC = NEW YORK
PL/SQL procedure successfully completed.
SQL>
Huh, kind of works (if that's what you asked). Of course, it is just an example, you'll have to modify it if you want to get something really smart (hint: DATE columns).
The only idea I have is to insert the record into a TEMP table:
CREATE OR REPLACE PROCEDURE xxx (pi_record IN TABLE_NAME%ROWTYPE) AS
TEXT VARCHAR2(10000) := NULL;
item VARCHAR2(1000);
TABLE_DOES_NOT_EXIST EXCEPTION;
PRAGMA EXCEPTION_INIT(TABLE_DOES_NOT_EXIST, -942);
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE TABLE_NAME_TMP';
EXCEPTION
WHEN TABLE_DOES_NOT_EXIST then null;
END;
EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE TABLE_NAME_TMP AS SELECT * FROM TABLE_NAME WHERE ROWNUM = 0';
DELETE FROM TABLE_NAME_TMP;
INSERT INTO TABLE_NAME_TMP VALUES pi_record;
FOR aCol IN (SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE table_name = 'TABLE_NAME' ORDER BY COLUMN_ID) LOOP
EXECUTE IMMEDIATE 'SELECT '||aCol.COLUMN_NAME||' FROM TABLE_NAME_TMP' INTO item;
TEXT := TEXT || CHR(13) || aCol.COLUMN_NAME || ' : ' || item;
END LOOP;
DBMS_OUTPUT.PUT_LINE ( TEXT );
END;
In case table TABLE_NAME has static attributes then you should skip dynamic DROP TABLE ... and CREATE GLOBAL TEMPORARY TABLE ... and create the TEMP table only once.
everyone!
I got a different approach to get the difference between records dynamically:
You just have to create the global variables on the package header as bellow:
v_NAME_OF_TABLE_new NAME_OF_TABLE%rowtype;
v_NAME_OF_TABLE_old NAME_OF_TABLE%rowtype;
then create the function on your pkg body that return a boolean even if a field is different:
function is_different(p_old NAME_OF_TABLE%rowtype, p_new NAME_OF_TABLE%rowtype)
return boolean
is
cursor cols is
select tb.COLUMN_NAME
from all_tab_columns tb
where tb.OWNER = 'DW'
and tb.TABLE_NAME = 'NAME_OF_TABLE'
order by tb.COLUMN_ID;
l_sql varchar2(4000);
l_new varchar2(4000);
l_old varchar2(4000);
begin
pkg_NAME.v_NAME_OF_TABLE_new := p_new;
pkg_NAME.v_NAME_OF_TABLE_old := p_old;
for reg in cols loop
l_sql := '
begin
:x := pkg_NAME.v_NAME_OF_TABLE_new.'||reg.COLUMN_NAME||';'||'
end;';
execute immediate l_sql using out l_new;
l_sql := '
begin
:x := pkg_NAME.v_NAME_OF_TABLE_old.'||reg.COLUMN_NAME||';'||'
end;';
execute immediate l_sql using out l_old;
--- dbms_output.put_line(l_new||' - '||l_old);
if nvl(l_new,'NULO') <> nvl(l_old,'NULO') then
return true;
end if;
end loop;
return false;
end;
Atention: This can turn your process heavier and slower.
That's all!
Hope this can be helpful!