searching a concatenated string in whole database - sql

I am trying to find a concatenated string in Whole Oracle Database Using Below Script
DECLARE
match_count INTEGER;
-- Type the owner of the tables you are looking at
-- v_owner VARCHAR2(255) :='APEX_030200';
-- 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) :='48-S-9-00028';
v_sql varchar2(1000);
BEGIN
FOR t IN (
SELECT table_name, column_name,owner
FROM sys.all_tab_cols
WHERE
data_type LIKE '%CHAR%'
AND owner NOT IN ('XDB','SYS','SYSMAN')
AND table_name <> 'Folder36_TAB'
) LOOP
--v_sql := 'SELECT COUNT(*) FROM '|| t.owner || '.'||t.table_name||' WHERE '||t.column_name || ' like ''%'' || :1 || ''%''';
v_sql := 'SELECT COUNT(*) FROM '|| t.owner || '.'||t.table_name||' WHERE '||t.column_name || ' = :1 ';
EXECUTE IMMEDIATE
v_sql
INTO match_count
USING v_search_string;
IF match_count > 0 THEN
dbms_output.put_line(v_sql);
dbms_output.put_line(t.owner||'.'|| t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(v_sql);
END;
/
The String 48-S-9-00028 is concatenation of 4 Columns
like 48 , S , 9 AND 00028 = 48-S-9-00028
I want to find the concatenated string through whole database that which 4 columns are making this string , I have tried from below scripts but its only giving output for a single value.

If the value being searched is from 4 concatenated cols, then your best bet doing a trawl is to search for each component value, since the value '48-S-9-00028' won't actually exist.
If the the values is obtained from a view with derived values, then you'll be out of luck. All is not lost though, see my comment about using AWR or trace to find what queries are being run.
So, searching for this I would change your trawl to the following (NOTE, this is functional only, might not be quick):
DECLARE
match_count INTEGER;
match_value VARCHAR2(20);
v_data_type VARCHAR2(255) :='VARCHAR2';
v_sql varchar2(1000);
BEGIN
FOR t IN (
SELECT table_name, column_name,owner
FROM sys.all_tab_cols
WHERE
data_type LIKE '%CHAR%'
AND owner NOT IN ('XDB','SYS','SYSMAN')
AND table_name <> 'Folder36_TAB'
) LOOP
v_sql := 'SELECT ''' || t.column_name || ''' cn '
'FROM '|| t.owner || '.'||t.table_name||
' WHERE '||t.column_name || ' IN ( ''48'', ''S'', ''9'', ''00028'' ) ' ||
' AND rownum < 2';
EXECUTE IMMEDIATE v_sql INTO match_value;
dbms_output.put_line(v_sql);
dbms_output.put_line(t.owner||'.'|| t.table_name ||' '||t.column_name||' '||match_value );
END IF;
END;
That is, search for any table where any of the 4 component values exist, return just the value matched and just the first row found to help you narrow your search.

" client have just shown me a report for which we dont have source code , so exactly dont know which 4 columns are being concatenated"
First of all, the client must have some idea of what data this report presents. This could give you some clue to what sort of tables you need to look at.
Secondly, if it's a business key - and if it isn't then why look for it - then just maybe there's a primary key or unique key defined for those columns. So, this query might produce some candidates:
select c.constraint_name, c.constraint_type
from user_cons_columns cc
join user_constraints c
on c.constraint_name = cc.constraint_name
group by c.constraint_name, c.constraint_type having count(cc.column_name) = 4;
Both these suggests assume a sensible naming convention and proper enforcement of relational integrity. Under the circumstances you might not be that lucky.
In which case, enable SQL Trace, run the report then peruse the trace file to see all the queries which provide the data. Your table will be one of those. Find out more.

Related

Disable all related constraints from a table with Oracle PL/SQL

CREATE OR REPLACE PROCEDURE disable_all_constraints
(p_owner IN VARCHAR2,
p_table_name IN VARCHAR2)
IS
CURSOR check_status IS
(SELECT status
FROM all_constraints
WHERE owner = p_owner
AND table_name = p_table_name );
v_status VARCHAR2(10);
BEGIN
OPEN check_status;
FETCH check_status
INTO v_status;
IF (check_status %NOTFOUND)
THEN
DBMS_OUTPUT.PUT_LINE('No Constraints added to the table, give me another table to process!');
ELSE
FOR cur IN (SELECT owner, constraint_name , table_name
FROM all_constraints
WHERE owner = p_owner
AND table_name = p_table_name)
LOOP
EXECUTE IMMEDIATE 'ALTER TABLE '||cur.owner||'.'||cur.table_name||' MODIFY CONSTRAINT '||cur.constraint_name||' DISABLE CASCADE ';
END LOOP;
END IF;
CLOSE check_status;
SELECT DISTINCT status
INTO v_status
FROM all_constraints
WHERE owner = p_owner
AND table_name = p_table_name;
IF v_status = 'DISABLED'
THEN
DBMS_OUTPUT.PUT_LINE('All related constraints disable succesufully!');
ELSE
DBMS_OUTPUT.PUT_LINE('Something went wrong, but that is impossible!');
END IF;
END;
So i wrote the above piece. I tested it and it does the job pretty well i think. But my question is, how correct is it or how bad is it? Can it be done in a much easier and optimal way so to speak? Thanks.
There is nothing intrinsically wrong with the way you're doing this but there are a number of potential issues and some unnecessary code.
Your first cursor is unnecessary, you're going to loop through ALL_CONSTRAINTS anyway - if you don't enter the loop then the table has no constraints
You may attempt to disable already disabled constraints - you only need to select those constraints that are currently enabled.
Your final select to see whether there are any non-disabled constraints will always fail if there is more than one status as you'll get a TOO_MANY_ROWS exception (for returning more than one row). This means that your final ELSE statement will never be entered.
It's highly unlikely that a constraint will not be disabled without an exception being raised. I wouldn't even bother with the final check.
More generally, there's rarely a point in outputting information to screen. It requires someone to read it. It's normally better if everything just works, or an exception is raised in the event of an error.
I would simplify this massively to something like the following, which simply loops through the non-disabled constraints and disables them.
create or replace procedure disable_all_constraints (
p_owner in varchar2
, p_table_name in varchar2
) is
begin
for cur in ( select owner || '.' || table_name as object
, constraint_name
from all_constraints
where owner = p_owner
and table_name = p_table_name
and status <> 'DISABLED'
) loop
execute immediate 'alter table ' || cur.object || '
modify constraint ' || cur.constraint_name || '
disable cascade';
end loop;
end;
If you feel like you have to have the extra checks and printing this can be done more cleanly:
create or replace procedure disable_all_constraints (
p_owner in varchar2
, p_table_name in varchar2
) is
l_has_constraint boolean := False;
l_ct number;
begin
for cur in ( select owner || '.' || table_name as object
, constraint_name
from all_constraints
where owner = p_owner
and table_name = p_table_name
and status <> 'DISABLED'
) loop
l_has_constraint := True;
execute immediate 'alter table ' || cur.object || '
modify constraint ' || cur.constraint_name || '
disable cascade';
end loop;
if not l_has_constraint then
dbms_output.put_line('No Constraints added to the table.');
else
select count(*) into l_ct
from all_constraints
where owner = p_owner
and table_name = p_table_name
and status <> 'DISABLED'
;
if l_ct = 0 then
dbms_output.put_line('All related constraints disable successfully');
else
dbms_output.put_line('Something went wrong, but that is impossible');
end if;
end if;
end;

Get max(length(column)) for all columns in an Oracle table

I need to get the maximum length of data per each column in a bunch of tables. I'm okay with doing each table individually but I'm looking for a way to loop through all the columns in a table at least.
I'm currently using the below query to get max of each column-
select max(length(exampleColumnName))
from exampleSchema.exampleTableName;
I'm basically replacing the exampleColumnName with each column in a table.
I've already went through 3-4 threads but none of them were working for me either because they weren't for Oracle or they had more details that I required (and I couldn't pick the part I needed).
I'd prefer to have it in SQL than in PLSQL as I don't have any create privileges and won't be able to create any PLSQL objects.
Got the below query to work -
DECLARE
max_length INTEGER; --Declare a variable to store max length in.
v_owner VARCHAR2(255) :='exampleSchema'; -- Type the owner of the tables you are looking at
BEGIN
-- loop through column names in all_tab_columns for a given table
FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and table_name = 'exampleTableName') LOOP
EXECUTE IMMEDIATE
-- store maximum length of each looped column in max_length variable
'select nvl(max(length('||t.column_name||')),0) FROM '||t.table_name
INTO max_length;
IF max_length >= 0 THEN -- this isn't really necessary but just to ignore empty columns. nvl might work as well
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||max_length ); --print the tableName, columnName and max length
END IF;
END LOOP;
END;
Do let me know if the comments explain it sufficiently, else I'll try to do better. Removing table_name = 'exampleTableName' might loop for all tables as well, but this is okay for me right now.
You can try this; although it uses PL/SQL it will work from within SQL-Plus. It doesn't loop. Hopefully you don't have so many columns that the SELECT query can't fit in 32,767 characters!
SET SERVEROUTPUT ON
DECLARE
v_sql VARCHAR2(32767);
v_result NUMBER;
BEGIN
SELECT 'SELECT GREATEST(' || column_list || ') FROM ' || table_name
INTO v_sql
FROM (
SELECT table_name, LISTAGG('MAX(LENGTH(' || column_name || '))', ',') WITHIN GROUP (ORDER BY NULL) AS column_list
FROM all_tab_columns
WHERE owner = 'EXAMPLE_SCHEMA'
AND table_name = 'EXAMPLE_TABLE'
GROUP BY table_name
);
EXECUTE IMMEDIATE v_sql INTO v_result;
DBMS_OUTPUT.PUT_LINE(v_result);
END;
/

SQL Select Like Column Name from table

The goal is to create a Oracle Function that is capable of query column name off a token provided by the user as to create a function with such capabilities
select cols_like('%e%') from table
This is the point I am currently at
CREATE OR REPLACE Function COLS_LIKE
(v_search in VARCHAR2, v_table in VARCHAR2)
RETURN VARCHAR
IS
TYPE r_cursor IS REF CURSOR;
c_emp r_cursor;
crs_cols VARCHAR(255);
column_list VARCHAR(1000);
BEGIN
OPEN c_emp FOR
'select COLUMN_NAME from cols
where TABLE_NAME = ''' || v_table || '''
and column_name like ''' || v_search || '''';
LOOP
FETCH c_emp INTO crs_cols;
EXIT WHEN c_emp%NOTFOUND;
if column_list IS NULL THEN
column_list := crs_cols;
else
column_list := column_list || ', ' || crs_cols;
end if;
END LOOP;
RETURN column_list;
END;
Where you call the function such as this
Declare
tests VARCHAR(100);
sql_stmt VARCHAR2(200);
begin
tests := COLS_LIKE('%E%', 'table');
DBMS_OUTPUT.PUT_LINE(tests);
-- OR
sql_stmt := 'select ' || COLS_LIKE('%E%', 'table') || ' from table';
DBMS_OUTPUT.PUT_LINE(sql_stmt);
end;
The end goal would be something such as this
select COLS_LIKE('%E%', 'table') from table;
What modifications can I make to my function or how I am calling to so that this function can be applied correctly.
Why you'd want to do such a thing I've no idea but you could return an open cursor to PL/SQL fairly easily:
create or replace function cols_like (
PTable in varchar2
, PColumn in varchar2
) return sys_refcursor
l_cols varchar2(32767);
c_curs sys_refcursor;
begin
select listagg(column_name, ', ') within group (order by column_id)
into l_cols
from user_tab_cols
where table_name = upper(Ptable)
and column_name like '%' || upper(PColumn) || '%'
;
open c_curs for '
select ' || l_cols || '
from ' || Ptable;
return c_curs;
end;
/
Returning this to a standard SQL statement will be a lot more difficult, this is because in selecting this function you're only selecting one column's worth of data. You want to be able to select N columns, which means you need to start returning nested tables that have been dynamically created.
I'm sure it's possible; but, before you get anywhere close to starting to attempt to do this think about why you're doing it. Ask a question where you don't state your end goal but where you state what your actual problem is. Chances are there's a lot simpler solution.
I was also having same problem and found this, and it is working for me. I was just making comparison of mobile numbers from two tables and in one of the tables some numbers have 0 in start and some don't. Finally got solution and just added % in start:
a.number was having numbers, in some of them starting 0 was missing. and b.number was accurate.
b.number like CONCAT('%',a.number)

Retrieving Column Names and Data types in PLSQL

I am writing a PL SQL block that retrieves all the columns and the data types of the tables in the database. I am able to get the columns , but not the datatypes. Looking for suggestions for a good approach. Any help would be appreciated. My code is as follows
ACCEPT p_1 PROMPT 'Please enter the Table Name'
DECLARE
v_table_name VARCHAR2(40) :='&p_1';
-- First cursor
CURSOR get_tables IS
SELECT DISTINCT table_name
FROM user_tables
WHERE UPPER(table_name) = UPPER(v_table_name);
--Second cursor
CURSOR get_columns IS
SELECT DISTINCT column_name
FROM user_tab_columns
WHERE table_name = v_table_name;
v_column_name VARCHAR2(100);
-- Third Cursor
CURSOR get_types IS
SELECT data_type
FROM user_tab_columns
WHERE table_name = v_table_name;
v_data_type user_tab_columns.data_type%type;
BEGIN
-- Open first cursor
OPEN get_tables;
FETCH get_tables INTO v_table_name;
DBMS_OUTPUT.PUT_LINE(' ');
DBMS_OUTPUT.PUT_LINE('Table = ' || v_table_name );
DBMS_OUTPUT.PUT_LINE('=========================');
CLOSE get_tables;
-- Open second cursor
OPEN get_columns;
FETCH get_columns INTO v_column_name;
WHILE get_columns%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(' ' || v_column_name);
FETCH get_columns INTO v_column_name;
END LOOP;
CLOSE get_columns;
--Open Third Cursor
OPEN get_types;
FETCH get_types into v_data_type;
WHILE get_types%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(' ' || v_data_type );
FETCH get_types into v_data_type;
END LOOP;
CLOSE get_types;
END;
My error states PLS-00371: at most one declaration for 'V_DATA_TYPE' is permitted
Not a PLSQL guru but here's my grain.
Select data_type from user_tab_columns where TABLE_NAME = 'YourTableName'
Props to Eric, check this thread and his answer.
Remember you can use DESC command to describe an Oracle Table, View, Synonym, package or Function. It will give you name, data_type and lengh.
And if this actually works for you, you should be able to get the data for all of your tables, although I'm not a huge fan of cursors, you should do fine.
Try this:
-- Open second cursor
OPEN get_columns;
LOOP
FETCH get_columns INTO v_column_name, v_data_type;
EXIT WHEN get_columns%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(' ' || v_column_name);
END LOOP;
CLOSE get_columns;
END LOOP;
But be careful on the datatype you've chosen for v_data_type variable.
Good effort, but too much code. you need a short vacation :)
SELECT table_name,
column_name,
data_type,
data_length,
nullable
FROM cols
WHERE table_name='&YOUR_TABLE'
ORDER BY column_id
Your block has way too much code. This is all you need:
begin
for r in ( select column_name, data_type
from user_tab_columns
where table_name = upper('&&p_1')
order by column_id )
loop
dbms_output.put_line(r.column_name ||' is '|| r.data_type );
end loop;
end;
I encountered a similar problem. It can be viewed here: Retrieving Table Structure with Dynamic SQL.
Of things to note, I made sure that if data_scale = 0 I indicated it was to be a Integer, and if it was >0, it was a Double.

writing a generic procedure in oracle

i want to write procedure which accents name of 2 tables as arguments and then compare the number or rows of the 2.
Also i want to each field of the 2 columns.The row which has a missmatch shold be
moved to another error table.
Can anyone give a PL/SQL procedure for doing this.
I want to achive this in oracle 9
Pablos example wont work, the idea is right though.
Something like this do it.
create or replace PROCEDURE COMPARE_ROW_COUNT(T1 IN VARCHAR2, T2 IN VARCHAR2) AS
v_r1 number;
v_r2 number;
v_sql1 varchar2(200);
v_sql2 varchar2(200);
BEGIN
v_sql1 := 'select count(1) from ' || T1;
v_sql2 := 'select count(1) from ' || T2;
EXECUTE IMMEDIATE v_sql1 into v_r1;
EXECUTE IMMEDIATE v_sql2 into v_r2;
dbms_output.put_line(T1 || ' count = ' || v_r1 || ', ' || T2 || ' count = ' || v_r2);
END;
DBMS_SQL is your friend for such operations.
You can use dynamic sql in PL/SQL. EXECUTE IMMEDIATE is your friend.
So, if you take two table names and trying to compare their row counts, you would do something like:
CREATE OR REPLACE PROCEDURE COMPARE_ROW_COUNT(T1 IN VARCHAR2(200), T2 IN VARCHAR2(200)) AS
v_cursor integer;
v_r1 integer;
v_r2 integer;
v_sql varchar2(200);
BEGIN
v_sql := "select count(1) into :1 from " || T1;
EXECUTE IMMEDIATE v_sql USING v_r1;
v_sql := "select count(1) into :1 from " || T2;
EXECUTE IMMEDIATE v_sql USING v_r2;
-- compare v_r1 and v_r2
END;
Not 100% sure about PL/SQL syntax. It's been a while since the last time I coded in great PL/SQL!
You can achieve same results with similar approach using DBMS_SQL. Syntax is a little bit more complicated though.
I am just posting here to note that all answers gravitate around dynamic SQL, and do not turn the attention to the implied problems using it.
Consider passing the following string as first or second parameter:
dual where rownum = 0 intersect
SELECT 0 FROM dual WHERE exists (select 1 from user_sys_privs where UPPER(privilege) = 'DROP USER')
I'll leave it to that.
To answer your question - Oracle actually stores these values in the data dictionary, so if you have access to it:
CREATE OR REPLACE PROCEDURE COMPARE_ROW_COUNT(T1 IN VARCHAR2, T2 IN VARCHAR2) AS
v_text varchar2(1000);
BEGIN
select listagg(owner || ' ' || table_name || ' count = ' || num_rows, ',')
into v_text
from all_tables --user, all or dba tables depends on requirements
where table_name in (T1, T2);
dbms_output.put_line(v_text);
exception
when others then raise; -- Put anything here, as long as you have an exception block
END COMPARE_ROW_COUNT;