I have two queries for disabling all constraints, but they don't seem to be working.
first one disables foreign keys:
select 'alter table '||table_name||' disable constraint '||constraint_name||';'
from user_constraints
where constraint_type ='R'
and status = 'ENABLED';
and the second disables everything else:
select 'alter table '||table_name||' disable constraint '||constraint_name||';'
from user_constraints
where status = 'ENABLED';
Now when I check the constraints with SELECT * FROM USER_CONSTRAINTS
I can see that they all are still 'ENABLED'.
Why is this?
I tried commit after running the queries but to no avail.
My goal is to disable constraints from all tables with those queries.
As per my comment above, it isn't sufficient to run the 2 queries, you then need to run all the alter table statements that these have generated. However you could do it all at once using PL/SQL. I have combined the 2 queries into one, using order by to process the foreign keys (constraint_type = 'R') first:
begin
for r in
( select 'alter table '||table_name||' disable constraint '||constraint_name as statement
from user_constraints
where status = 'ENABLED'
order by case constraint_type when 'R' then 1 else 2 end
)
loop
execute immediate r.statement;
end loop;
end;
Related
I need to temporarily disable all constraints and triggers of a schema in order to anonymize data in several tables. As there are dependencies between tables, I prefer to disable everything and once the anonymization treatment is over I can enable all constraints and triggers one more time.
I tried SET FOREIGN_KEY_CHECKS=0; and I got this error:
ERROR: unrecognized configuration parameter "foreign_key_checks"
SQL state: 42704
I've been reading a lot about that and some people say this is not possible.
Do you know a way to do that?
Thank you!
To disable foreign keys and deferrable unique and primary key constraints for a table, you can use
ALTER TABLE ... DISABLE TRIGGER ALL;
To disable all such constraints for the duration of a database session, you can
SET session_replication_role = replica;
Both tricks won't work for non-deferrable constraints and check constraints.
I found this solution,
I created a temporal table to keep all constraints definition:
CREATE TEMPORARY TABLE temp_constraints AS
SELECT conname constraintname, conrelid::regclass tablename, pg_get_constraintdef(oid) definition, contype
FROM pg_catalog.pg_constraint;
Then I drop all constraints:
DO $$
DECLARE constraint_name TEXT;
DECLARE constraint_table TEXT;
BEGIN
FOR constraint_name, constraint_table IN
SELECT constraintname , tablename FROM temp_constraints ORDER BY contype DESC
LOOP
EXECUTE 'ALTER TABLE ' || constraint_table || ' DROP CONSTRAINT IF EXISTS ' || constraint_name || ' CASCADE;';
END LOOP;
END $$;
And after anonymizing data I restore all constraints using the definitions in the temporal table and I drop the temporal table:
DO $$
DECLARE constraint_table TEXT;
DECLARE constraint_definition TEXT;
BEGIN
FOR constraint_table, constraint_definition IN
SELECT tablename, definition FROM temp_constraints ORDER BY contype DESC
LOOP
EXECUTE 'ALTER TABLE ' || constraint_table || ' ADD ' || constraint_definition || ';';
END LOOP;
DROP TABLE IF EXISTS temp_constraints;
END $$;
I hope this can help someone else. Thank you!
How to clean table space correctly?
For example, we have a table with several million rows and functional indexes. We want to remove the most part of the table.
For this we called: delete from some_table where ....
What the next steps?
Is this sequence correct?
1. Drop functional indexes.
2. Alter some_table shrink space.
3. Create functional indexes again.
As you probably realized (or you wouldn't be asking about function-based indexes in particular), you are not able to simply:
alter table mytable enable row movement;
alter table mytable shrink space;
alter table mytable disable row movement;
Attempting to do so will result in:
ORA-10631: SHRINK clause should not be specified for this object
(Note: this limitation also applies to bitmap join indexes.)
Clearly, you can drop the FBI first...
drop index my_fbi_index;
alter table mytable enable row movement;
alter table mytable shrink space;
alter table mytable disable row movement;
create index my_fbi_index... online;
That's not an online operation though. Your application(s) will be affected by the missing function-based-index for a short time.
If you need an online operation and you are on Oracle 12.2 or later, you can try this instead:
alter table mytable move online;
(alter table...move (no "online") is available pre-12.2, but it's not an online operation and it will drop your index segments, leaving the indexes marked "unusable" and requiring you to rebuild them. So, not really a good option pre-12.2.)
Best way
Create a new table with only the valid data, and recreate the indexes there, then drop the old table.
Create the new table with the filtered data, where table_name is the name of the table, filter_text is a condition started with 'WHERE ...', and the partitionText is a partitioning clause if you have one for the table, example RANGE (ENDEDAT) INTERVAL ( NUMTODSINTERVAL(1,''day'') ) ( PARTITION p_first VALUES LESS THAN ( TO_DATE(''01-01-2010'',''dd-MM-yyyy'') ) ) ENABLE ROW MOVEMENT
sqlCommand := 'create table ' || table_name ||'_TMP
tablespace &TBS_NORMAL_TABLES initrans 32 ' || partitionText ||'
nologging
AS (SELECT * FROM '||table_name|| ' ' ||filter_text||')';
EXECUTE IMMEDIATE sqlCommand;
Add attributes to the new table.
Such as, constraints, indexes... Those could be collected from built-in tables such as all_constraints, all_indexes. The moving of the attributes could be also automatized, just need to apply some rename hacks.
Change the old and the new table
execute immediate 'ALTER TABLE &Schemaowner..'||v_table_name||' RENAME TO '||v_table_name||'_OT';
execute immediate 'ALTER TABLE &Schemaowner..'||v_table_name||'_TP'||' RENAME TO '||v_table_name;
Drop the old table
execute immediate 'DROP TABLE '||v_table_name||'_OT';
TL;DR informations about the shrinking, and reorg tables
Here comes some informations and useful links about my investigation when considered archiving huge amount of data on live production DBs.
Automated way to shrink some tables, and handle an error on them
for i in (SELECT obj.owner,obj.table_name,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable, row_movement
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND obj.owner = &Schemaowner
and obj.table_name like 'T_%' and obj.table_name not like 'TMP_%'
and NVL(idx.cnt,0) < 1)
loop
BEGIN
if i.row_movement='ENABLED' then
execute immediate 'alter table '||i.table_name||' shrink space';
else
execute immediate 'alter table '||i.table_name||' enable row movement';
execute immediate 'alter table '||i.table_name||' shrink space';
execute immediate 'alter table '||i.table_name||' disable row movement';
end if;
DBMS_OUTPUT.PUT_LINE('shrinked table: ' || i.table_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error while shrinking table: ' || i.table_name);
DBMS_OUTPUT.PUT_LINE (SQLERRM);
DBMS_OUTPUT.PUT_LINE (SQLCODE);
if SQLCODE=-10635 then
for p in (SELECT partition_name ,tablespace_name FROM user_tab_partitions WHERE table_name = 'SOME_PARTITIONED_TABLE')
loop
BEGIN
execute immediate 'alter table '||i.table_name||' MOVE PARTITION ' || p.partition_name || ' ONLINE TABLESPACE ' || p.tablespace_name || ' COMPRESS UPDATE INDEXES';
DBMS_OUTPUT.PUT_LINE('moved partition: ' || p.partition_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error while moving partition: ' || p.partition_name);
DBMS_OUTPUT.PUT_LINE (SQLERRM);
DBMS_OUTPUT.PUT_LINE (SQLCODE);
CONTINUE;
END;
end loop;
end if;
CONTINUE;
END;
end loop;
USEFUL selects
Shrinkable tables
SELECT obj.owner
,obj.table_name
,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND NVL(idx.cnt,0) < 1
and obj.owner='YOUR_SCHEMA_OWNER'
Indexes that makes shrink command inexecutable
SELECT *
FROM all_indexes
WHERE index_type LIKE 'FUN%'
and owner='YOUR_SCHEMA_OWNER'
The shrinkable tables without any compromise
SELECT obj.owner
,obj.table_name
,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND NVL(idx.cnt,0) < 1
--and obj.table_name like 'T_%' and obj.table_name not like 'TMP_%'
and obj.compression != 'ENABLED'
and obj.table_name not in (SELECT table_name FROM user_tab_partitions WHERE compression = 'ENABLED')
and obj.owner='YOUR_SCHEMA_OWNER'
Shrink problems, compression of tables and/or partitions
SELECT table_name,compression, compress_for FROM user_tables WHERE compression = 'ENABLED'
SELECT table_name,partition_name, compression, compress_for FROM user_tab_partitions WHERE compression = 'ENABLED' ORDER BY 1
Examine these before/after shrink, test the hell out of it
select segment_name,bytes/1024/1024 as mb,blocks from user_segments where segment_name='TABLE_NAME'
In my case i create a table (not partitioned) with couple million rows, then delete 1/3 of it, here is the results:
|| BYTES || BLOCKS ||
Before deletion || 105250816 || 12848 ||
After deletion || 78774272 || 9616 ||
Investigation and side-effects
Possible side effects
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9536157800346457013
...and when should use reorg:
http://www.dba-oracle.com/t_table_fragmentation.htm
...enable row movement while shrink space can reorder the rows (this means if use use ROWID based jobs or selects or something like that, then there could be some surprises)
http://www.dba-oracle.com/t_enable_row_movement.htm
create table newtable
select * from oldtable where...
drop oldtable
rename newtable to oldtable
recreate indexes.
Create a copy of your table
Drop old table
Rename copy
has a disadvantage, during this operation (which may take some time) your application is not available.
So, your approach
Drop functional indexes.
Alter some_table shrink space.
Create functional indexes again.
is correct.
In case you have a partitioned table, see this one: https://dba.stackexchange.com/questions/162415/how-to-shrink-space-on-table-with-a-function-based-index
There is another option I haven't seen anyone propose. Issue the DELETE, then do nothing. WHY do we think we need to rebuild the index? WHY do we think we need to resize the table? If we do nothing after the DELETE, all of the extents for the table remain allocated to the table and WILL be used on future INSERTS. If you have a retail store and conduct a clearance sale, resulting in a lot of empty shelves, do you then rebuild the store to eliminate the 'wasted' space? Or do you simply re-use the empty shelves as new stock comes in? What is to be gained by resizing the table? At best, it will release the space back to the TS, not the OS file system. While there are use cases that argue for resizing the table, it is NOT an automatic given that resizing after a large (even massive) DELETE is necessarily justified.
So again, my answer is to do nothing after the DELETE (well, do a COMMIT, of course!)
I have the following query which will delete the constraint as i did not know the constraint name, i am getting it from select query by specifying table name and column name.
ALTER TABLE <CHILD_TABLE_NAME>
DROP CONSTRAINT in (SELECT
CONS.CONSTRAINT_NAME
FROM USER_CONSTRAINTS CONS
LEFT JOIN USER_CONS_COLUMNS COLS ON COLS.CONSTRAINT_NAME = CONS.CONSTRAINT_NAME
LEFT JOIN USER_CONSTRAINTS CONS_R ON CONS_R.CONSTRAINT_NAME = CONS.R_CONSTRAINT_NAME
LEFT JOIN USER_CONS_COLUMNS COLS_R ON COLS_R.CONSTRAINT_NAME = CONS.R_CONSTRAINT_NAME
WHERE CONS.CONSTRAINT_TYPE = 'R'
AND COLS.COLUMN_NAME='<COLUMN_NAME>'
AND CONS.TABLE_NAME = '<CHILD_TABLE_NAME>')
when i am runnning above query , getting the below error in pl/sql developer
ORA-02250:missing or invalid constraint name
You should check out "Oracle Database SQL Language Reference" to see if "ALTER TABLE" command has in its syntax the ability to have a subquery in it, which it doesn't. Example --> http://docs.oracle.com/database/121/SQLRF/statements_3001.htm#SQLRF01001
"alter table" command, "drop_constraint_clause" example from the provided link
To sum up all, the answer is no you cannot have a subquery specified in a "alter table" command.
What you can do is do it with dynamic SQL as #J. Chomel specified.
Example :
BEGIN
FOR i in (<<YOUR SELECT>>)
LOOP
EXECUTE IMMEDIATE 'ALTER TABLE '||i.TABLE_NAME||' DROP CONSTRAINT '||i.CONSTRAINT_NAME;
END LOOP;
END;
I need to modify a table to remove the primary key constraint, the only problem is the table is generated by another system and I don't have the constraint name. So as a workaround I have created the following script which I thought should work but it's not, so can anyone please help me to write another script which will work.
ALTER TABLE temp
MODIFY CONSTRAINT (select constraint_name
FROM all_constraints
WHERE owner like '%tempUser%' and
table_name like '%temp%' and
constraint_type = 'P'
)
DISABLE;
I am getting following error when trying to run.
SQL Error: ORA-14006: invalid partition name
14006. 00000 - "invalid partition name"
*Cause: a partition name of the form is
expected but not present.
*Action: enter an appropriate partition name.
I tried following code based on suggested link and it run without any error but it does not remove the constraint.
BEGIN
FOR c IN
(SELECT c.owner, c.table_name, c.constraint_name
FROM all_constraints c
where owner like '%tempUser%' and table_name like '%temp%' and constraint_type = 'P')
LOOP
dbms_utility.exec_ddl_statement('alter table ' || c.table_name || ' modify CONSTRAINT ' || c.constraint_name || ' disable ');
END LOOP;
END;
/
You could use dynamic SQL to solve this. First find the name of your constraint and then create dynamic code to disable it.
Have a look at Disable all table constraints in Oracle, which is similar
I have a Firebird table like this:
CREATE TABLE events (
event VARCHAR(6) NOT NULL
CHECK (event IN ('deploy', 'revert', 'fail')),
change_id CHAR(40) NOT NULL,
change VARCHAR(512) NOT NULL
);
Now I need to add another value to the IN() list in the CHECK constraint. How do I do that?
Things I've tried so far:
Updating the value in RDB$TRIGGERS.RDB$TRIGGER_SOURCE:
UPDATE RDB$TRIGGERS
SET RDB$TRIGGER_SOURCE = 'CHECK (event IN (''deploy'', ''revert'', ''fail'', ''merge''))'
WHERE RDB$TRIGGER_SOURCE = 'CHECK (event IN (''deploy'', ''revert'', ''fail''))';
Does not seem to work, as the trigger is compiled in RDB$TRIGGERS.RDB$TRIGGER_BLR.
Creating a new table with a new check, copying the data over, dropping the old table and renaming the new table. However, it seems that one cannot rename a Firebird table, so I can't make the new table have the same name as the old one.
I suspect updating RDB$TRIGGERS is the way to go (idk!), if only I could get Firebird to recompile the code. But maybe there's a better way?
You need to drop and the re-create the check constraint.
As you didn't specify a name for your constraint, Firebird created one, so you first need to find that name:
select trim(cc.rdb$constraint_name), trg.rdb$trigger_source
from rdb$relation_constraints rc
join rdb$check_constraints cc on rc.rdb$constraint_name = cc.rdb$constraint_name
join rdb$triggers trg on cc.rdb$trigger_name = trg.rdb$trigger_name
where rc.rdb$relation_name = 'EVENTS'
and rc.rdb$constraint_type = 'CHECK'
and trg.rdb$trigger_type = 1;
I just added the trigger source for informational reasons.
Once you have the name, you can drop it, e.g.
alter table events drop constraint integ_27;
and then add the new constraint:
alter table events
add constraint check_event_type
CHECK (event IN ('deploy', 'revert', 'fail', 'merge'));
In the future you don't need to look for the constraint name because you already it.
Here's how to do it dynamically:
SET AUTOddl OFF;
SET TERM ^;
EXECUTE BLOCK AS
DECLARE trig VARCHAR(64);
BEGIN
SELECT TRIM(cc.rdb$constraint_name)
FROM rdb$relation_constraints rc
JOIN rdb$check_constraints cc ON rc.rdb$constraint_name = cc.rdb$constraint_name
JOIN rdb$triggers trg ON cc.rdb$trigger_name = trg.rdb$trigger_name
WHERE rc.rdb$relation_name = 'EVENTS'
AND rc.rdb$constraint_type = 'CHECK'
AND trg.rdb$trigger_type = 1
INTO trig;
EXECUTE STATEMENT 'ALTER TABLE EVENTS DROP CONSTRAINT ' || trig;
END^
SET TERM ;^
COMMIT;
ALTER TABLE events ADD CONSTRAINT check_event_type CHECK (
event IN ('deploy', 'revert', 'fail', 'merge')
);
COMMIT;
I had to disable AUTOddl and put in explicit commits or else I got a deadlock on the ALTER TABLE ADD CONSTRAINT statement.
Here's how to do it dynamically:
EXECUTE BLOCK RETURNS (STMT VARCHAR(1000)) AS
BEGIN
SELECT TRIM(R.RDB$CONSTRAINT_NAME)
FROM RDB$RELATION_CONSTRAINTS R
WHERE R.RDB$RELATION_NAME = 'TABLE_NAME'
AND UPPER(R.RDB$CONSTRAINT_TYPE) = UPPER('PRIMARY KEY')
INTO :STMT;
IF (:STMT IS NOT NULL) THEN
BEGIN
EXECUTE STATEMENT 'ALTER TABLE TABLE_NAME DROP CONSTRAINT ' || :STMT || ';';
EXECUTE STATEMENT 'ALTER TABLE TABLE_NAME ADD CONSTRAINT ' || :STMT || ' PRIMARY KEY (FIELD1, FIELD2, FIELD3);';
END
ELSE
BEGIN
EXECUTE STATEMENT 'ALTER TABLE FIELD1 ADD CONSTRAINT PK_PRIMARY_NAME PRIMARY KEY (FIELD1, FIELD2, FIELD3);';
END
END;