About create table as select (CTAS) - oracle

When we do:
create table big2 as select * from big1;
Are the indexes and constraints also copied over to the new table?

Only NOT NULL constraints are copied. See FAQ.
You can do CREATE TABLE big2 (bigid PRIMARY KEY) AS SELECT * FROM big1 tp create a primary key, but yes, for other indexes you'll want to copy and run the index creation scripts.

Just for info, there is a simple way to remember indices to recreate them after deleting source table:
SELECT DBMS_METADATA.get_ddl('INDEX', index_name)
FROM user_indexes
WHERE TABLE_NAME = 'BIG1';

Related

Right way to create index organized table

I am trying to create a index organized table in oracle 11. I create the index organized table and insert the row from another table.
create table salIOT (
mypk ,
cid ,
date,
CONSTRAINT sal_pk PRIMARY KEY (mypk)
) ORGANIZATION INDEX
AS Select * from another table;
But the leaf blocks are empty when I query
SQL> Select owner, index_name, table_name, leaf_blocks from all_indexes where table_name like 'SALIOT';
Am I missing something here?
You still need to gather statistics on the table, something like:
exec dbms_stats.gather_table_stats('ABC', 'IOTTableName')
(assuming 'ABC' is your username; change as needed) - then re-run your SELECT against ALL_INDEXES and you will see how many leaf blocks you have.

How to check if table has specific grants before inserting a record in a table?

Scenario: I have a config table which includes meta-data about log tables (e.g. Table_name, table_owner). I want to add a trigger to this config table which essentially checks if the table record that is to be inserted, has the specific grant (delete) if not, then don't allow that to happen. How can I go about this?
Another way to do it is to forget about triggers but use a VIEW...WITH CHECK OPTION to control that no one inserts data they shouldn't.
Suppose your config table looks like this:
CREATE TABLE my_config
( owner VARCHAR2(30) NOT NULL,
table_name VARCHAR2(30) NOT NULL,
other_stuff VARCHAR2(500),
CONSTRAINT my_config_pk PRIMARY KEY ( owner, table_name ) );
Create a view to handle inserts
The conditions of the view limit it to only include tables on which the current user has DELETE privileges.
The WITH CHECK OPTION will ensure than no one may use the view to insert or update data that does not satisfy the view.
CREATE VIEW my_config_ins_v
AS
SELECT * FROM my_config c
WHERE EXISTS ( SELECT 'user has delete privs'
FROM user_tab_privs p
WHERE p.owner = c.owner
AND p.grantee = user
AND p.table_name = c.table_name
AND p.privilege = 'DELETE' )
WITH CHECK OPTION;
Try it out
INSERT INTO my_config_ins_v VALUES ('USER_1','TABLE_I_HAVE_ACCESS_TO', 'STUFF');
-- 1 row inserted.
INSERT INTO my_config_ins_v VALUES ('SYS','OBJ$', 'STUFF');
-- ORA-01402: view WITH CHECK OPTION where-clause violation
Naturally, for this to be effective, you cannot
GRANT INSERT ON my_config TO anyone; -- don't do this
Instead:
GRANT INSERT ON my_config_ins_v TO anyone;

Alter table enable novalidate constraint

I am trying to add UNIQUE KEY on a previously existing table with duplicate records by setting ENABLE NOVALIDATE.
But I am getting ORA-02299: cannot validate (my_owner.my_key_UK ) - duplicate keys found
ALTER TABLE my_owner.my_table
ADD CONSTRAINT my_key_UK UNIQUE (ID1,ID2)
ENABLE NOVALIDATE;
A unique constraint uses an index to enforce the noduplicates rule. By default it will create a unique index (makes sense right?). It is this index creation which is hurling ORA-02299.
However, if this is an existing index on the constrained columns the constraint will use that. The good news is, the index doesn't need to be unique for the constraint to use it.
So what you need to do is build a non-unique index first:
create index whatever_idx on my_table (ID1,ID2);
Then you will be able to create your constraint:
ALTER TABLE my_owner.my_table
ADD CONSTRAINT my_key_UK UNIQUE (ID1,ID2)
ENABLE NOVALIDATE;
You can check this by querying the data dictionary:
select uc.constraint_name
, uc.constraint_type
, uc.index_name
, ui.uniqueness as idx_uniqueness
from user_constraints uc
join user_indexes ui
on ui.index_name = uc.index_name
where uc.table_name = 'MY_TABLE'
Oracle ensure unique values using indexes. And if you create unique constrains db automatic creates unique index. Workaround is add DEFERRABLE options. In this case oracle creates normal index. Check example.
create table table_abc (a number,b number);
insert into table_abc values(1,1);
insert into table_abc values(1,2);
ALTER TABLE table_abc ADD CONSTRAINT my_key_a UNIQUE (a) DEFERRABLE enable novalidate; -- no error but in table nonunique values
ALTER TABLE table_abc ADD CONSTRAINT my_key_b UNIQUE (b) ENABLE NOVALIDATE; --no error
select * from user_indexes where table_name ='TABLE_ABC';

How to set default value for column of new created table from select statement in 11g

I create a table in Oracle 11g with the default value for one of the columns. Syntax is:
create table xyz(emp number,ename varchar2(100),salary number default 0);
This created successfully. For some reasons I need to create another table with same old table structure and data. So I created a new table with name abc as
create table abc as select * from xyz.
Here "abc" created successfully with same structure and data as old table xyz. But for the column "salary" in old table "xyz" default value was set to "0". But in the newly created table "abc" the default value is not set.
This is all in Oracle 11g. Please tell me the reason why the default value was not set and how we can set this using select statement.
You can specify the constraints and defaults in a CREATE TABLE AS SELECT, but the syntax is as follows
create table t1 (id number default 1 not null);
insert into t1 (id) values (2);
create table t2 (id default 1 not null)
as select * from t1;
That is, it won't inherit the constraints from the source table/select. Only the data type (length/precision/scale) is determined by the select.
The reason is that CTAS (Create table as select) does not copy any metadata from the source to the target table, namely
no primary key
no foreign keys
no grants
no indexes
...
To achieve what you want, I'd either
use dbms_metadata.get_ddl to get the complete table structure, replace the table name with the new name, execute this statement, and do an INSERT afterward to copy the data
or keep using CTAS, extract the not null constraints for the source table from user_constraints and add them to the target table afterwards
You will need to alter table abc modify (salary default 0);
new table inherits only "not null" constraint and no other constraint.
Thus you can alter the table after creating it with "create table as" command
or you can define all constraint that you need by following the
create table t1 (id number default 1 not null);
insert into t1 (id) values (2);
create table t2 as select * from t1;
This will create table t2 with not null constraint.
But for some other constraint except "not null" you should use the following syntax
create table t1 (id number default 1 unique);
insert into t1 (id) values (2);
create table t2 (id default 1 unique)
as select * from t1;

How to duplicate all data in a table except for a single column that should be changed

I have a question regarding a unified insert query against tables with different data
structures (Oracle). Let me elaborate with an example:
tb_customers (
id NUMBER(3), name VARCHAR2(40), archive_id NUMBER(3)
)
tb_suppliers (
id NUMBER(3), name VARCHAR2(40), contact VARCHAR2(40), xxx, xxx,
archive_id NUMBER(3)
)
The only column that is present in all tables is [archive_id]. The plan is to create a new archive of the dataset by copying (duplicating) all records to a different database partition and incrementing the archive_id for those records accordingly. [archive_id] is always part of the primary key.
My problem is with select statements to do the actual duplication of the data. Because the columns are variable, I am struggling to come up with a unified select statement that will copy the data and update the archive_id.
One solution (that works), is to iterate over all the tables in a stored procedure and do a:
CREATE TABLE temp as (SELECT * from ORIGINAL_TABLE);
UPDATE temp SET archive_id=something;
INSERT INTO ORIGINAL_TABLE (select * from temp);
DROP TABLE temp;
I do not like this solution very much as the DDL commands muck up all restore points.
Does anyone else have any solution?
How about creating a global temporary table for each base table?
create global temporary table tb_customers$ as select * from tb_customers;
create global temporary table tb_suppliers$ as select * from tb_suppliers;
You don't need to create and drop these each time, just leave them as-is.
You're archive process is then a single transaction...
insert into tb_customers$ as select * from tb_customers;
update tb_customers$ set archive_id = :v_new_archive_id;
insert into tb_customers select * from tb_customers$;
insert into tb_suppliers$ as select * from tb_suppliers;
update tb_suppliers$ set archive_id = :v_new_archive_id;
insert into tb_suppliers select * from tb_suppliers$;
commit; -- this will clear the global temporary tables
Hope this helps.
I would suggest not having a single sql statement for all tables and just use and insert.
insert into tb_customers_2
select id, name, 'new_archive_id' from tb_customers;
insert into tb_suppliers_2
select id, name, contact, xxx, xxx, 'new_archive_id' from tb_suppliers;
Or if you really need a single sql statement for all of them at least precreate all the temp tables (as temp tables) and leave them in place for next time. Then just use dynamic sql to refer to the temp table.
insert into ORIGINAL_TABLE_TEMP (SELECT * from ORIGINAL_TABLE);
UPDATE ORIGINAL_TABLE_TEMP SET archive_id=something;
INSERT INTO NEW_TABLE (select * from ORIGINAL_TABLE_TEMP);

Resources