Problem
I have sql scripts which may use different tablespaces for different database users.
In order to remain flexible with the table creation I'd like to keep only 1 script and apply it to the various users. For that purpose I have something like this:
Tablespaces:
CREATE TABLESPACE MY_TABLESPACE DATAFILE 'MY_TABLESPACE.dat' SIZE 40M ONLINE;
CREATE TABLESPACE MY_INDEXSPACE DATAFILE 'MY_INDEXSPACE.dat' SIZE 40M ONLINE;
And the table creation script:
define default_tablespace = 'MY_TABLESPACE';
define default_indexspace = 'MY_INDEXSPACE';
drop table test_table;
create table test_table ( id number ) tablespace &default_tablespace;
create index my_index on test_table( id) tablespace &default_indexspace;
i. e. I can't set a default tablespace for the user, because the index uses a different tablespace.
Question
Is it possible to override the definition of default_tablespace and default_indexspace depending on e. g. an environment variable?
Something like:
define default_tablespace = isEnviromentVariableSet( 'OTHER_TABLESPACE') ? getEnvironmentVariable( OTHER_TABLESPACE) : 'MY_TABLESPACE';
That way I could use different tablespaces whenever I invoke the script externally by some utility and at the same time I could keep the default tablespace.
Thank you very much for the help!
In DDL operation (create, drop, etc.) u can't use variables.
Easy way is use pl/sql anonymous block like this.
declare
my_tabable_space varchar2(100) default 'my_some_tablespace';
other_tablespace varchar2(100);
begin
DBMS_SYSTEM.get_env('OTHER_TABLESPACE',other_tablespace);
if other_tablespace is not null then
my_tabable_space := other_tablespace ;
end if;
execute immediate 'create table test_table ( id number ) tablespace' || my_tabable_space;
end;
/
and for select ENV variable u can use DBMS_SYSTEM.get_env ('NAME of VARIABLE', my_variable) but this package need DBA right (i think.. :-) )
Related
how to move indexes to another tablespace
select owner, index_name, TABLE_NAME, a.TABLESPACE_NAME
from all_indexes a where a.TABLE_NAME = 'REFUND';
ALTER INDEX CBS.PK_REFUND_ID REBUILD TABLESPACE T_IDX;
ORA-14086: A partitioned index may not be rebuilt as a whole.
I can't execute this statement because I don't know in which partition the index is
ALTER INDEX PK_REFUND_ID REBUILD PARTITION xxxxxx TABLESPACE T_IDX;
Because you also need partition name info which can be reached from user[all/dba]_tab_partitions dictionary view. If you got the related partition name, then
ALTER INDEX PK_REFUND_ID REBUILD PARTITION PR_REFUND_OLD [ONLINE];
might be used just to rebuild the index.
or
ALTER INDEX PK_REFUND_ID REBUILD PARTITION PR_REFUND_OLD [ONLINE] TABLESPACE T_IDX;
might be used to move the index of the partition to a different tablespace. Using ONLINE option would suit well for the tables currently having DML activity.
Use the following code in order to run as a batch job
DECLARE
v_ts_name VARCHAR2(35) := 'T_IDX';
BEGIN
FOR c IN
(
SELECT 'ALTER INDEX '||i.index_name||' REBUILD PARTITION '||p.partition_name||
' ONLINE TABLESPACE '||v_ts_name AS command
FROM user_part_indexes i
JOIN user_ind_partitions p
ON p.index_name = i.index_name
AND i.table_name = 'REFUND'
AND p.tablespace_name != v_ts_name
)
LOOP
EXECUTE IMMEDIATE c.command;
END LOOP;
END;
/
Oracle - Alter all table column names with trim of white space in between names
For suppose column names before alter :
Home number
Mobile number
Local number
After alter column names shall be :
Homenumber
Mobilenumber
Localnumber
I've tried this way: but unable to crack:
UPDATE SA_VW_PHONENUMBER TN SET TN.Column_Name = TRIM (TN.Column_Name);
Fully automatic way
Use this cursor based DDL hacking - statement concat.
BEGIN
FOR alters IN
(
SELECT
'ALTER TABLE "'||table_name||'" RENAME COLUMN "'||column_name||
'" TO "'||replace(cols.column_name,' ','')||'"' sql_stmt
FROM all_tab_cols cols
WHERE REGEXP_LIKE(column_name,'[[:space:]]')
AND owner = user --Add real schema name here
ORDER BY 1
) LOOP
DBMS_OUTPUT.PUT_LINE ( alters.sql_stmt ||';') ;
EXECUTE IMMEDIATE alters.sql_stmt;
END LOOP;
END;
/
If you want to use the safe way
As I know you cannot perform a DDL as a dynamic SQL, so you cannot pass variables to the ALTER TABLE command, but here is what you can do instead of that.
Selecting the occurences:
SELECT table_name,column_name,replace(cols.column_name,' ','') as replace_name
FROM all_tab_cols
WHERE REGEXP_LIKE(column_name,'[[:space:]]');
Use the ALTER TABLE DDL command:
alter table T_TABLE rename column "COLUMN SPACE" TO "COLUMNNOSPACE";
Try the REPLACE function
UPDATE SA_VW_PHONENUMBER TN SET TN.Column_Name = REPLACE(TN.Column_Name,' ','')
How to create a global temporary table with same table structure to that of a existing table?
I know this concept is available in SQL server like "select * into #temp123 from abc". But I want to perform the same in Oracle.
Create global temporary table mytemp
as
select * from myTable
where 1=2
Global temporary tables in Oracle are very different from temporary tables in SQL Server. They are permanent data structures, it is merely the data in them which is temporary (limited to the session or transaction, depending on how a table is defined).
Therefore, the correct way to use global temporary tables is very different to how we use temporary tables in SQL Server. The CREATE GLOBAL TEMPORARY TABLE statement is a one-off exercise (like any other table). Dropping and recreating tables on the fly is bad practice in Oracle, which doesn't stop people wanting to do it.
Given the creation of a global temporary table should a one-off exercise, there is no real benefit to using the CREATE TABLE ... AS SELECT syntax. The statement should be explicitly defined and the script stored in source control like any other DDL.
You have tagged your question [oracle18c]. If you are really using Oracle 18c you have a new feature open to you, private temporary tables, which are closer to SQL Server temporary tables. These are tables which are genuinely in-memory and are dropped automatically at the end of the transaction or session (again according to definition). These are covered in the Oracle documentation but here are the headlines.
Creating a private temporary table data with a subset of data from permanent table T23:
create table t23 (
id number primary key
, txt varchar2(24)
);
insert into t23
select 10, 'BLAH' from dual union all
select 20, 'MEH' from dual union all
select 140, 'HO HUM' from dual
/
create private temporary table ORA$PTT_t23
on commit preserve definition
as
select * from t23
where id > 100;
The ORA$PTT prefix is mandatory (although it can be changed by setting the init.ora parameter PRIVATE_TEMP_TABLE_PREFIX, but why bother?
There after we can execute any regular DML on the table:
select * from ORA$PTT_t23;
The big limitation is that we cannot use the table in static PL/SQL. The table doesn't exist in the data dictionary as such, and so the PL/SQL compiler hurls - even for anonymous blocks:
declare
rec t23%rowtype;
begin
select *
into rec
from ORA$PTT_t23';
dbms_output.put_line('id = ' || rec.id);
end;
/
ORA-06550: line 6, column 10: PL/SQL: ORA-00942: table or view does not exist
Any reference to a private temporary table in PL/SQL must be done with dynamic SQL:
declare
n pls_integer;
begin
execute immediate 'select id from ORA$PTT_t23' into n;
dbms_output.put_line('id = ' || n);
end;
/
Basically this restricts their usage to SQL*Plus (or sqlcl scripts which run a series of pure SQL statements. So, if you have a use case which fits that, then you should check out private temporary tables. However, please consider that Oracle is different from SQL Server in many aspects, not least its multi-version consistency model: readers do not block writers. Consequently, there is much less need for temporary tables in Oracle.
In the SQL Server's syntax the prefix "#" (hash) in the table name #temp123 means - create temporary table that is only accessible via the current session ("##" means "Global").
To achive exactly the same thing in Oracle you can use private temporary tables:
SQL> show parameter private_temp_table
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
private_temp_table_prefix string ORA$PTT_
create table mytab as
select 1 id, cast ('aaa' as varchar2 (32)) name from dual
;
create private temporary table ora$ptt_mytab on commit preserve definition as
select * from mytab where 1=0
;
Private TEMPORARY created.
Afterwards you can use these tables in SQL and PL/SQL blocks:
declare
r mytab%rowtype;
begin
insert into ora$ptt_mytab values (2, 'bbb');
select id + 1, name||'x' into r from ora$ptt_mytab where rownum = 1;
insert into ora$ptt_mytab values r;
end;
/
select * from mytab
union all
select * from ora$ptt_mytab;
ID NAME
---------- --------------------------------
1 aaa
2 bbb
3 bbbx
Some important restrictions on private temporary tables:
The name must always be prefixed with whatever is defined with the parameter PRIVATE_TEMP_TABLE_PREFIX. The default is ORA$PTT_.
You cannot reference PTT in the static statements of the named PL/SQL blocks, e.g. packages, functions, or triggers.
The %ROWTYPE attribute is not applicable to that table type.
You cannot define column with default values.
I need to truncate a table that has around 40 million records in it. i guess it is ok to REUSE STORAGE for the table since new data will be inserted after the truncate.
I need to know
approximately how much time the command
TRUNCATE TABLE <tablename> REUSE STORAGE; would take.
Can this be done offline, so that the operations/ commands on the DB
don't get affected?
Based on "so that the operations/ commands don't get affected", it sounds like you'd like to replace the contents of a table with "good" values, without anyone seeing an empty table. If users/processes need to be able to continue making changes to the table while you switch out the contents, you'll need to research DBMS_REDEFINITION. If this is a read-only table (in other words, you can do this operation while nobody is inserting/updating/deleting its contents), there's a nice DIY approach that takes advantage of partition exchanging:
1) Create a table which has identical partitioning and indexing as original, and has the desired "good" rows.
2)
IF YOUR TABLE IS PARTITIONED:
CREATE TABLE <tablename>_TMP AS SELECT * FROM <tablename> WHERE 1=0;
-- Add any local indexes on <tablename> as standard indexes on <tablename_tmp>
BEGIN
FOR R IN (SELECT * FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = <tablename>)
LOOP
EXECUTE IMMEDIATE
'INSERT INTO <tablename>_TMP'
||' SELECT * FROM <good_data_tablename> PARTITION ('||R.PARTITION_NAME||')';
EXECUTE IMMEDIATE
'ALTER TABLE <tablename>_TMP'
||' EXCHANGE PARTITION WITH TABLE <tablename> PARTITION ' || R.PARTITION_NAME
||' INCLUDING INDEXES WITHOUT VALIDATION UPDATE GLOBAL INDEXES';
EXECUTE IMMEDIATE
'TRUNCATE TABLE <tablename>_TMP';
END LOOP
END;
/
DROP TABLE <tablename>_TMP ;
IF YOUR TABLE IS NOT PARTITIONED:
CREATE TABLE <tablename>_TMP
PARTITION BY RANGE (<non-null-column>)
(PARTITION ALL_DATA values less than (maxvalue))
AS SELECT * FROM <good_data_tablename>;
-- Add any <tablename> indexes as local indexes on <tablename>_TMP
ALTER TABLE <tablename> EXCHANGE PARTITION WITH TABLE <tablename>_TMP INCLUDING INDEXES WITHOUT VALIDATION;
DROP TABLE <tablename>_TMP ;
I've already tried out a tool named TOYS. I found it free but unfortunately it didn't work.
Then, I tried "RED-Gate Schema Compare for Oracle" but it uses the technique to drop and recreate the table mean while I need to just alter the table with the newly added/dropped columns.
Any help is highly appreciated
Thanks
Starting from Oracle 11g you could use dbms_metadata_diff package and specifically compare_alter() function to compare metadata of two schema objects:
Schema #1 HR
create table tb_test(
col number
)
Schema #2 HR2
create table tb_test(
col_1 number
)
select dbms_metadata_diff.compare_alter( 'TABLE' -- schema object type
, 'TB_TEST' -- object name
, 'TB_TEST' -- object name
, 'HR' -- by default current schema
, 'HR2'
) as res
from dual;
Result:
RES
-------------------------------------------------
ALTER TABLE "HR"."TB_TEST" ADD ("COL_1" NUMBER);
ALTER TABLE "HR"."TB_TEST" DROP ("COL");