there is a table with about 800 columns that I have to update to upper case, do you know a "quick" way to do it?
Maybe with PLSQL but I can't think of how to do it.
Thanks!
Probably something like this would work. Using xmlagg because listagg would run out of space. You could also do a PL/SQL loop over user_tab_columns for your table if you needed something more detailed.
declare
my_table varchar2(30) := 'TABLE1';
command clob;
begin
select to_clob('update ') || my_table || ' set '
|| rtrim(xmlagg(xmlelement(e,column_name || '=UPPER(' || column_name || '), ') order by column_id).extract('//text()').getclobval(),', ') as cols
into command
from user_tab_columns where table_name = my_table;
execute immediate command;
end;
/
Try running the select statement by itself first (with your table name, and no into) if you want to run the update statement yourself.
Related
In another question I tried to create a hist table, which keeps a log from the given table. With the answers in that question, I tried to create something new.
Since it is not possible to create a system trigger on tables or views, I created a DDL trigger like this:
create or replace trigger ident_hist_trig after alter on schema
declare
v_table varchar2(30);
begin
select upper(ora_dict_obj_name) into v_table from dual;
if (v_table = 'Z_IDENT') then
prc_create_hist_tabel('z_ident_hist', 'z_ident');
elsif (v_table = 'D_IDENT') then
prc_create_hist_tabel('d_ident_hist', 'd_ident');
elsif (v_table = 'X_IDENT') then
prc_create_hist_tabel('x_ident_hist', 'x_ident');
else
null;
end if;
end;
/
The procedure prc_create_hist_tabel looks like this:
create or replace procedure prc_create_hist_tabel(p_naam_hist_tabel in varchar2, p_naam_tabel in varchar2) is
cursor c is
select 'alter table ' || p_naam_hist_tabel || ' add ' || column_name || ' ' || data_type || case when data_type = 'DATE' then null else '(' || data_length || ')' end lijn
from user_tab_columns
where TABLE_NAME = upper(p_naam_tabel)
and column_name not in (select column_name from user_tab_columns where table_name = upper(p_naam_hist_tabel));
v_dummy number(1);
cursor trig is
select column_name || ',' kolom, ':old.' || column_name || ',' old
from user_tab_columns
where table_name = upper(p_naam_tabel);
v_trigger_sql varchar2(32767);
begin
begin
select 1 into v_dummy
from user_tab_columns
where TABLE_NAME = upper(p_naam_hist_tabel)
group by 1;
exception when no_data_found then
execute immediate 'create table ' || p_naam_hist_tabel || ' (wijziger varchar2(60) default user, wijzigdatum date default sysdate, constraint pk_' || p_naam_hist_tabel || ' primary key (wijziger, wijzigdatum))';
end;
dbms_output.put_line('BBB');
for i in c
loop
begin
dbms_output.put_line(i.lijn);
execute immediate i.lijn;
exception when others then
dbms_output.put_line(i.lijn);
end;
end loop;
v_trigger_sql := 'create or replace trigger ' || p_naam_tabel || '_hist_trig after update on ' || p_naam_tabel || ' for each row begin insert into ' || p_naam_hist_tabel || ' (';
for v_lijn in trig
loop
v_trigger_sql := v_trigger_sql || v_lijn.kolom;
end loop;
v_trigger_sql := substr(v_trigger_sql, 1, length(v_trigger_sql) - 1);
v_trigger_sql := v_trigger_sql || ') values (';
for v_lijn in trig
loop
v_trigger_sql := v_trigger_sql || v_lijn.old;
end loop;
v_trigger_sql := substr(v_trigger_sql, 1, length(v_trigger_sql) - 1);
v_trigger_sql := v_trigger_sql || '); end;';
execute immediate v_trigger_sql;
end;
/
In short what that function does, is maintain the history table. If it doesn't exist, it will create one, and if it exists, it will add the new columns to it. The procedure also creates a new trigger which will write the old values into the history table after update.
But when I alter one of the tables x_ident, z_ident or d_ident, the cursor c will return nothing (I can check that with the print when I loop through it). Although when execute the select after I altered my table, then I do get results.
The results I get from altering the table d_ident are these:
BBB
d_ident: Table altered.
But I guess it should be the other way around, I think that the procedure prc_create_hist_tabel is executed before the alter table actually goes off, and I guess I should get something like this:
d_ident: Table altered.
BBB
Any help would be apreciated. I tried to create a trigger on insert on user_tab_columns, but that gave me ORA-25001: cannot create this trigger type on views.
I tried with a sleep command as well, but that didn't work either.
This won't work. Even if you were able to get the column that is being added to the table in your trigger, if you tried to actually do DDL in a trigger, you'd get an error that DDL isn't allowed in a trigger.
I'd expect that the right way to approach this would be to make the call to prc_create_hist_tabel as part of your promotion scripts. Reasonable systems don't add columns to tables willy-nilly. The DDL is part of a promotion that exists in source control and gets deployed after testing. If your promotion scripts failed to modify the history table, you'd find out during testing that you missed a step and the change would never go to production. Having changes happen automatically means that they're not in change control which makes it more difficult to do a build from change control.
If you are determined to do this automatically, your trigger would need to submit a job, realistically using dbms_job not the newer dbms_scheduler, that calls the procedure. That job would run after the transaction the DDL trigger is a part of committed. At that point, the column would be visible in dba_tab_columns. And your job is free to do DDL.
I inherited a large database and nobody seems to know which table/column a particular data set is coming from. I've spent a lot of time going through table by table in Oracle's SQL Developer, but I can't find it. Is there a way in SQLDeveloper to search the entire table for a single value. Something like:
select table_name from all_tab_columns where column_value='desired value';
The db has around 1K+ tables each with lots of columns so manually combing through this isn't working.
You can use the following script to search for a value in all columns of your schema. The execution time for the script will depend on the number of tables in your schema and the number of rows in each of your table.
Replace 'abc' with the value which you intend to search. Also, right now the script will search all VARCHAR2 columns. You can also insert the table names and counts into a table instead of doing a DBMS_OUTPUT.PUT_LINE.
DECLARE
CURSOR cur_tables
IS
SELECT table_name,
column_name
FROM user_tab_columns
WHERE data_type = 'VARCHAR2';
v_sql VARCHAR2(4000);
v_value VARCHAR2(50);
v_count NUMBER;
BEGIN
v_value := 'abc';
FOR c_tables IN cur_tables LOOP
v_sql := 'SELECT count(1) FROM ' || c_tables.table_name || ' WHERE ' || c_tables.column_name || ' = :val' ;
EXECUTE IMMEDIATE v_sql INTO v_count USING v_value;
IF v_count > 0 THEN
DBMS_OUTPUT.PUT_LINE('Table Name ' || c_tables.table_name || ' Column Name ' || c_tables.column_name || ' Row Count ' || v_count);
END IF;
END LOOP;
END;
A table TMP has 5 partitions, namely P_1, P_2,....P_5.
I need to drop some partitions of TMP; the partitions to drop are derived by another query.
Ex:
ALTER TABLE TMP DROP PARTITIONS (SELECT ... From ... //expression to get partition names )
Let's say the SELECT statement returns P_1 & P_5. The part query of the ALTER statement above doesn't work. Is there any way to drop partitions with input from a SELECT statement?
You can use dynamic sql in anonymous pl/sql block;
Begin
for i in (select part_name from ... //expression to get partition names) loop
execute immediate 'ALTER TABLE TMP DROP PARTITION ' || i.part_name;
end loop;
end;
For dropping multiple partitions on a go then;
declare
v_part varchar(1000);
Begin
select LISTAGG(partition_name, ', ') WITHIN GROUP (ORDER BY partition_name DESC)
into v_part
from ... //expression to get partition names;
execute immediate 'ALTER TABLE TMP DROP PARTITION ' || v_part;
end;
You may use the following sql to generate DML for dropping multiple table partitions.
select 'ALTER TABLE ' || TABLE_OWNER || '.' || TABLE_NAME || ' DROP PARTITION ' || '"' || PARTITION_NAME || '";' from DBA_TAB_PARTITIONS
where TABLE_NAME='%YOUR_PATTERN%'order by PARTITION_NAME;
You'll need to use dynamic SQL. Something this:
begin
for prec in (SELECT ... From ... //expression to get partition names )
loop
execute immediate 'ALTER TABLE TMP DROP PARTITION '
|| prec.partition_name;
end loop;
end;
/
Clearly you need to have complete faith that your query will return only the partitions you want to drop. Or equivalent faith in your Backup & Recover plans :)
Alternatively you can use a similar approach to generate a drop script which you can review before you run it.
You have to use pl/sql block for dropping partitions from a table with select query. Use listagg for making a comma separated list.
DECLARE
var1 varchar2(50);
BEGIN
SELECT listagg(Partition_names) into var1 from table_name//expression to get partition names ;
execute immediate 'alter table tmp drop PARTITION'||var1 ;
END;
Example on listagg
select LISTAGG(partition_name,',') within group(order by table_name) as comma_list
from ALL_TAB_PARTITIONS where TABLE_owner='OWNER' AND TABLE_NAME='TABLE_NAME'
Maybe it's could somebody help.
This script drop all partitions for all partition tables for specific schema. I use it with clear DB with METADATA, for changing started (referencial) partition.
ALTER TABLE SCHEMA_1.TABLE_1
SET INTERVAL ();
ALTER TABLE SCHEMA_1.TABLE_2
SET INTERVAL ();
ALTER TABLE SCHEMA_1.TABLE_3
SET INTERVAL ();
set lines 100
set heading off
spool runme.sql
select 'ALTER TABLE ' || TABLE_OWNER || '.' || TABLE_NAME || ' DROP PARTITION ' || '"' || PARTITION_NAME || '";' from DBA_TAB_PARTITIONS
where
TABLE_OWNER='SCHEMA_1'
-- and TABLE_NAME='TABLE_%'
and PARTITION_NAME LIKE 'SYS_P%'
;
#runme
ALTER TABLE SCHEMA_1.TABLE_1
SET INTERVAL (NUMTOYMINTERVAL(1,'MONTH'));
ALTER TABLE SCHEMA_1.TABLE_1
SET INTERVAL (NUMTOYMINTERVAL(1,'MONTH'));
ALTER TABLE SCHEMA_1.TABLE_3
SET INTERVAL (NUMTOYMINTERVAL(1,'MONTH'));
Yes, the script is semi-manual, but I think it's more safety.
ALTER TABLE ... INTERVAL it's need for droping last partition.
Interval must be same that it was before
DECLARE
v_owner varchar2(40);
v_table_name varchar2(40);
cursor get_tables is
select distinct table_name
, user
from user_tables
where lower(user) = 'schema_name'
;
BEGIN
OPEN get_tables;
LOOP
FETCH get_tables
INTO v_table_name
, v_owner
;
EXIT WHEN get_tables%NOTFOUND;
EXECUTE IMMEDIATE
'INSERT
INTO STATS_TABLE
( TABLE_NAME
, SCHEMA_NAME
, RECORD_COUNT
, CREATED
)
SELECT '''
|| v_table_name
|| ''' , ''' || v_owner
|| ''' , COUNT(*)
, TO_DATE(SYSDATE,''DD-MON-YY'')
FROM ' || v_table_name
;
END LOOP;
CLOSE get_tables;
END;
I am using this to get row counts of all tables in a schema. I got this query from stackoverflow.
I ran this as a procedure its successfully compiled but i am unable to view the result y is it so?
I am new to pl/sql can anyone explain what does the select statement do after the excute immediate query I am unable to understand the logic behind.
Try this:
select TABLE_NAME, NUM_ROWS from dba_tables where owner = 'xxxxx'
You can simply get row count against tables by:
select owner, table_name, nvl(num_rows,-1)
from all_tables
order by nvl(num_rows,-1) desc
https://livesql.oracle.com/apex/livesql/file/content_EPJLBHYMPOPAGL9PQAV7XH14Q.html
The row counts are inserted into a table name STATS_TABLE. You need to run a
select *
from stats_table
after running the procedure
This procedure is running successfully
DECLARE
v_owner varchar2(40);
v_table_name varchar2(40);
cursor get_tables is
select distinct table_name, user
from user_tables
where lower(user) = 'coreown' ;
BEGIN
OPEN get_tables;
LOOP
FETCH get_tables
INTO v_table_name, v_owner;
EXIT WHEN get_tables%NOTFOUND;
EXECUTE IMMEDIATE 'INSERT
INTO STATS_TABLE
( TABLE_NAME
, SCHEMA_NAME
, RECORD_COUNT
, CREATED
)
SELECT ''' || v_table_name ||
''' , ''' || v_owner || ''' , COUNT(*)
, TO_DATE(SYSDATE,''DD-MON-YY'')
FROM ' || v_table_name;
END LOOP;
CLOSE get_tables;
END;
/
I tried at my end.
After executing USE COMMIT.So that it will save the change is DB.
Then check again.
Well, user_tables does not have a user column, so that query is invalid. Always test the queries in isolation -- you probably want all_tables or dba_tables. Also, do you think you need a DISTINCT on that query?
Use an implicit cursor instead of an explicit one wherever you can -- less code, less error prone in coding, and faster.
Do not store dates as character types -- you just insert sysdate, not a representation of it as a string.
Don't prefix variable names with "v_" -- just namespace them with the procedure or block name.
The Execute Immediate constructs a SQL statement that performs an insert into the stats_table (why does it have the word "table" in the name? That's redundant, surely?)
The query will be constructed as something like:
INSERT INTO STATS_TABLE
( TABLE_NAME
, SCHEMA_NAME
, RECORD_COUNT
, CREATED
)
SELECT 'MY_TABLE_NAME' , 'MY_USERNAME' , COUNT(*) , SYSDATE
FROM MY_TABLE_NAME;
In Oracle 10g, is there a way to do the following in PL/SQL?
for each table in database
for each row in table
for each column in row
if column is of type 'varchar2'
column = trim(column)
Thanks!
Of course, doing large-scale dynamic updates is potentially dangerous and time-consuming. But here's how you can generate the commands you want. This is for a single schema, and will just build the commands and output them. You could copy them into a script and review them before running. Or, you could change dbms_output.put_line( ... ) to EXECUTE IMMEDIATE ... to have this script execute all the statements as they are generated.
SET SERVEROUTPUT ON
BEGIN
FOR c IN
(SELECT t.table_name, c.column_name
FROM user_tables t, user_tab_columns c
WHERE c.table_name = t.table_name
AND data_type='VARCHAR2')
LOOP
dbms_output.put_line(
'UPDATE '||c.table_name||
' SET '||c.column_name||' = TRIM('||c.column_name||') WHERE '||
c.column_name||' <> TRIM('||c.column_name||') OR ('||
c.column_name||' IS NOT NULL AND TRIM('||c.column_name||') IS NULL)'
);
END LOOP;
END;
Presumably you want to do this for every column in a schema, not in the database. Trying to do this to the dictionary tables would be a bad idea...
declare
v_schema varchar2(30) := 'YOUR_SCHEMA_NAME';
cursor cur_tables (p_schema_name varchar2) is
select owner, table_name, column_name
from all_tables at,
inner join all_tab_columns atc
on at.owner = atc.owner
and at.table_name = atc.table_name
where atc.data_type = 'VARCHAR2'
and at.owner = p_schema;
begin
for r_table in cur_tables loop
execute immediate 'update ' || r.owner || '.' || r.table_name
|| ' set ' || r.column_name || ' = trim(' || r.column_name ||');';
end loop;
end;
This will only work for fields that are VARCHAR2s in the first place. If your database contains CHAR fields, then you're out of luck, because CHAR fields are always padded to their maximum length.