SQL Select Like Column Name from table - sql

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)

Related

searching a concatenated string in whole database

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.

How to create a view in a FOR loop in Oracle SQL

What I'm trying to do is create views based off a condition between two tables, and I want it to go through all tables that meet this condition.
I've been doing some research and I found that cursors would be helpful for this sort of thing, but I've been running into a "cursor out of scope" at line 15.
DECLARE
query_str VARCHAR2(32000);
CURSOR all_syn IS
SELECT SYNONYM_NAME, TABLE_NAME
FROM ALL_SYNONYMS
WHERE SYNONYM_NAME LIKE 'S!_AG!_%' ESCAPE '!';
CURSOR our_tables IS
SELECT TABLE_NAME
FROM ALL_TABLES
WHERE TABLE_NAME LIKE 'AG!_%1' ESCAPE '!';
BEGIN
query_str := 'CREATE OR REPLACE VIEW ' || LTRIM(all_syn.SYNONYM_NAME, 'S_') || 'AS
SELECT TO_CHAR(itemnum) itemnum,
TO_CHAR(keywordnum) keywordnum,
TO_CHAR(keysetnum) keysetnum,
MOD_BY_EMPLOYEE,
MOD_BY_PROCESS,
MOD_DATE_EMPLOYEE,
MOD_DATE_PROCESS
FROM all_syn.SYNONYM_NAME,
our_tables.TABLE_NAME
WHERE our_tables.TABLE_NAME = ' || LTRIM(all_syn.SYNONYM_NAME, 'S_');
FOR v_rec IN all_syn LOOP
IF (v_rec.TABLE_NAME LIKE 'KEYXITEM%') THEN
EXECUTE IMMEDIATE query_str;
END IF;
END LOOP;
END;
The reason I am doing this is because my company has tables that aren't directly connected to a certain 3rd party DB link, so they had me change the table names by putting a 1 at the end of the affected tables, creating synonyms for these tables with the DB link, and then make views of these synonyms with the original table name so that they now have the DB link and act as the original table so that we don't have to change any code. I have to join the synonym tables with the changed tables, because we added some attributes that the 3rd party tables don't have.
If anyone has any suggestions or advice, it would be greatly appreciated! I'm new to using dynamic sql and PL/SQL, so bear with me please.
EDIT:
So I've improved my code, and I feel like I'm getting closer to my desired results, however I'm getting this weird error:
line 28, column 52:
PLS-00357: Table,View Or Sequence reference 'ALL_TABLES.TABLE_NAME' not allowed in this context
Which doesn't make sense to me as I'm declaring it in the query.
BEGIN
FOR v_rec IN all_syn LOOP
IF (v_rec.TABLE_NAME LIKE 'KEYXITEM%') THEN
query_str := 'CREATE OR REPLACE VIEW ' || LTRIM(v_rec.SYNONYM_NAME, 'S_') || ' AS
SELECT itemnum AS item_num,
keywordnum AS key_word_num,
keysetnum AS key_set_num,
MOD_BY_EMPLOYEE,
MOD_BY_PROCESS,
MOD_DATE_EMPLOYEE,
MOD_DATE_PROCESS,
FROM ( SELECT TABLE_NAME
FROM ALL_TABLES
WHERE TABLE_NAME LIKE ' || '''AG!_%1''' || ' ESCAPE ' || '''!''' || '
AND ' || RTRIM(ALL_TABLES.TABLE_NAME, '1') ||' = ' || LTRIM(v_rec.SYNONYM_NAME, 'S_') || ') our_tables,
' || v_rec.SYNONYM_NAME;
-- EXECUTE IMMEDIATE query_str;
END IF;
dbms_output.put_line(query_str);
END LOOP;
END;
You cannot reference cursor like that. Move the query_str creation inside the FOR LOOP and reference the record variable.
EDIT: I've tried to fix the FROM/WHERE clause, but you might be missing a join condition there.
DECLARE
query_str VARCHAR2(32000);
CURSOR all_syn IS
SELECT SYNONYM_NAME, TABLE_NAME
FROM ALL_SYNONYMS
WHERE SYNONYM_NAME LIKE 'S!_AG!_%' ESCAPE '!';
CURSOR our_tables IS
SELECT TABLE_NAME
FROM ALL_TABLES
WHERE TABLE_NAME LIKE 'AG!_%1' ESCAPE '!';
BEGIN
FOR v_rec IN all_syn LOOP
IF (v_rec.TABLE_NAME LIKE 'KEYXITEM%') THEN
query_str := 'CREATE OR REPLACE VIEW ' || LTRIM(v_rec.SYNONYM_NAME, 'S_') || 'AS
SELECT TO_CHAR(itemnum) itemnum,
TO_CHAR(keywordnum) keywordnum,
TO_CHAR(keysetnum) keysetnum,
MOD_BY_EMPLOYEE,
MOD_BY_PROCESS,
MOD_DATE_EMPLOYEE,
MOD_DATE_PROCESS
FROM ' || v_rec.SYNONYM_NAME || ',
' || v_rec.TABLE_NAME || '
WHERE ' || v_rec.TABLE_NAME = ' || LTRIM(v_rec.SYNONYM_NAME, 'S_');
EXECUTE IMMEDIATE query_str;
END IF;
END LOOP;
END;

How to Refer to a Column by ID or Index Number

In Oracle PL/SQL, I have run a query and am trying to read through each column for each row one by one so I can concatenate them together with a delimiter (hard format requirement). The script is used on multiple tables of varying sizes, so the number of columns is not known in advance. I used
SELECT COUNT(column_name) INTO NumColumns FROM all_tabs_cols
WHERE table_name = Table_Array(i);
where Table_Array has already been defined. This is in the middle of a for loop and has successfully gotten me a total number of columns. Table_Cursor is a SELECT * statement. After this I am trying to do something like
FOR j IN 0..NumColumns-1 LOOP
FETCH TABLE_CURSOR.column(j) INTO DataValue;
DBMS_OUTPUT.PUT(DataValue || '/');
END LOOP
The above is pseudo code. It illustrates the concept I am after. I do not know PL/SQL well enough to know how to get a value like this out of a row. I am also worried about accidentally advancing the cursor while doing this. How can I accomplish this task?
You must use some form of dynamic SQL. Here is a quick example:
It builds the SQL statement that will select the '/' separated columns from the table you want. Then it uses dynamic SQL to run that SQL statement.
DECLARE
p_table_name VARCHAR2(30) := 'DBA_OBJECTS';
l_sql VARCHAR2(32000);
TYPE varchar2tab IS TABLE OF VARCHAR2(32000);
l_array varchar2tab;
BEGIN
SELECT 'SELECT ' || listagg(column_name,' ||''/''||') within group ( order by column_id ) || ' FROM ' || owner || '.' || table_name || ' WHERE ROWNUM <= 100'
INTO l_sql
FROM dba_tab_columns
where table_Name = 'DBA_OBJECTS'
group by owner, table_Name;
EXECUTE IMMEDIATE l_sql BULK COLLECT INTO l_array;
FOR i in l_array.first .. l_array.last LOOP
dbms_output.put_line(l_array(i));
END LOOP;
END;
This is how your code should look:
SELECT F1 || ', ' || F2 || ', ' || ... || ', ' || FN
FROM TABLE
NO LOOPS
Here is how you can generate code that does not use loops.
Note, if you want you can take out the where statement and generate the code for the whole database.
Test with just one table first.
SELECT 'SELECT '|| LISTAGG(COLUMN_NAME, ' || '', '' || ') || ' FROM '||TABLE_NAME as sql_stm
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME='tablename'
GROUP BY TABLE_NAME;

Alter every table of a schema that has a name like 'something'?

I'd like to know if it is possible to alter every single table in a schema that contains a column name like 'something' in Oracle DB.
You can use a loop to iterate over USER_TAB_COLUMNS and generate the SQL statement:
declare
l_SQL varchar2(4000);
begin
for cur in (
select table_name, column_name
from user_tab_columns utc
where upper(utc.column_name) like '%SOMETHING%')
loop
l_SQL := 'alter table ' || cur.table_name || ' drop column ' || cur.column_name;
dbms_output.put_line(l_SQL);
-- execute immediate l_SQL; -- UNCOMMENT TO RUN; DO NOT DO THIS IN PRODUCTION!
end loop;
end;
Yes this is possible. You have to dynamically create the DDL or DML and execute immediate out of a PL/SQL routine. With "alter" do you mean change the content of the tables columns or do you mean change the columns properties?
EDIT:
You can use Frank's Routine but for a column modify you do this.
l_SQL := 'alter table ' || cur.table_name ||
' modify (' || cur.column_name || ' varchar2(50)); ';
I agree with Frank to not blindly modify the columns, use the dbms output as a generated script.
EDIT2:
There is one more thing I realized. Table user_tab_columns gives you also columns of views. You could exclude them by joining with user_tables:
set serveroutput on
declare
l_SQL varchar2(4000);
begin
for cur in (
select utc.table_name, utc.column_name
from user_tab_columns utc
join user_tables ut on (UT.TABLE_NAME = utc.table_name)
where upper(utc.column_name) like '%SO')
loop
l_SQL := 'alter table ' || cur.table_name || ' modify (' || cur.column_name || ' varchar2(50)); ';
dbms_output.put_line(l_SQL);
-- execute immediate l_SQL; -- UNCOMMENT TO RUN; DO NOT DO THIS IN PRODUCTION!
end loop;
end;

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;