I ran into a curious problem. I am creating copies of currently existing tables and adding partitions to them.
The process is as follows:
Rename current constraints (can't drop them without dropping table itself because I will need data later)
Create a new partitioned table that structurally copies current one.
so I have MYTABLE (original) and PART_TABLE (new partitioned), including FKs
Copy data with INSERT INTO SELECT clause
Alter table with indexes and PKs
Rename tables so I end up with MYTABLE (new partitioned) and TRASH_TABLE (original)
In step 4 unfortunately, I got an error
ALTER TABLE MYTABLE ADD CONSTRAINT "PK_MYTABLE"
PRIMARY KEY ("MY_ID", "SEQUENCE")
USING INDEX LOCAL TABLESPACE INDEXSPACE;
SQL Error: ORA-00955: "name is already used by an existing object"
Now, I logically assumed that I simply forgot to rename the PK so I check TRASH_TABLE, but I see there the correctly renamed PK.
I also ran
SELECT *
FROM ALL_CONSTRAINTS
WHERE CONSTRAINT_NAME LIKE 'PK_MYTABLE'
and it returned 0 results. Same with table USER_CONSTRAINTS.
Renamed PKs are displaying correctly.
Another thing I noticed is that only PKs are locked this way. Adding FKs or UNIQUE constraints work just fine.
As stated in How to rename a primary key in Oracle such that it can be reused, the problem is that Oracle creates an index for the primary key. You need to rename the autogenerated index as well. As suggested by Tony Andrews, try
ALTER INDEX "PK_MYTABLE" RENAME TO "PK_MYTABLE_OLD";
Related
I have a Mircrosoft Sql Server database made up of about 8 tables that are all related that I am trying to update. To do this I create a number of temporary tables
"CREATE TABLE [vehicle_data].[dbo].[temp_MAINTENANCE_EVENT] (" +
"[maintenance_event_id] int," +
"[maintenance_computer_code_id] int," +
"[veh_eng_maintenance_id] int," +
"CONSTRAINT [PK_maintenance_event_id"] PRIMARY KEY CLUSTERED ([maintenance_event_id] ASC))";
Then after all the temporary tables have been created I drop the existing tables, rename the temporary tables, and add foreign keys and indexing to the new tables to speed up joins and querying.
The issue I'm having is the original primary key references are remaining. So when I go to update again I get
Exception: There is already an object named 'PK_maintenance_event_id'
in the database. Could not create constraint.
I'm wondering what is the best course of action is? Should I not set the primary key when I create the temporary table and instead add it to the table after it has been renamed? Or is there a way to rename constraints so that when I rename the table I can change the name of the primary key constraint.
After the original tables have been dropped I want there to be as little downtime as possible, but anything that happens before the tables have been dropped can take a really long time and it won't matter.
If your temp tables need that constraint
When create
use
CONSTRAINT [PK_maintenance_event_id_temp"]
instead of
CONSTRAINT [PK_maintenance_event_id]
when you rename temp back to real table
exec sp_rename [PK_maintenance_event_id_temp], [PK_maintenance_event_id]
I have table which has 600 million records and also has the Partition on PS_TRPdate(TRPDate) column, I want to change it to another Partition PS_LPDate(LPDate).
So I have tried with small amount of data with following steps.
1) Drop the Primary key Constraints.
2) Adding the New Primary Key Clustered Index with new Partition PS_LPDate(LPDate).
Is it Feasible with 600 million records? Can anyone guide me for it?
and How does it works with Non Partitioned Tables?
--343
My gut feeling is that you should create a parallel table using a new primary key, file groups and files.
To test out my assumption, I looked at a old blog post in which I stored the first five million prime numbers into three files / file groups.
I used the TSQL view that Kalen Delaney wrote and I modified to my standards to look at the partition information.
As you can see, we have three partitions based on the primary key.
Next, I drop the primary key on the my_value column, create a new column named chg_value, update it to the prime number, and then try to create a new primary key.
-- drop primary key (pk)
alter table tbl_primes drop constraint [PK_TBL_PRIMES]
-- add new field for new pk
alter table tbl_primes add chg_value bigint not null default (0)
-- update new field
update tbl_primes set chg_value = my_value
-- try to add a new primary key
alter table tbl_primes add constraint [PK_TBL_PRIMES] primary key (chg_value)
First, I was surprise that the partition still stayed together after dropping the PK. However, the view shows the index no longer exists.
Second, I end up receiving the following error during constraint creation.
While you could merge/switch the partitions into one file group which is not part of the scheme, drop/create the primary key, partition function & partition scheme, and then move the data yet again with the appropriate merge/switch statements, I would not.
This will generate a ton of work (TSQL) and cause alot of I/O on the disks.
I suggest you build a parallel partitioned table, if you have space, with the new primary key. Reload the data from the old table to the new.
If you are not using data compression and have the enterprise version of SQL Server, why not save the bytes by turning it on.
Good luck!
John
www.craftydba.com
I know that PostgreSQL tables that use a SERIAL primary key end up with an implicit index, sequence and constraint being created by PostgreSQL. The question is how to rename these implicit objects when the table is renamed. Below is my attempt at figuring this out with specific questions at the end.
Given a table such as:
CREATE TABLE foo (
pkey SERIAL PRIMARY KEY,
value INTEGER
);
Postgres outputs:
NOTICE: CREATE TABLE will create implicit sequence "foo_pkey_seq" for serial column "foo.pkey"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
Query returned successfully with no result in 52 ms.
pgAdmin III SQL pane shows the following DDL script for the table (decluttered):
CREATE TABLE foo (
pkey serial NOT NULL,
value integer,
CONSTRAINT foo_pkey PRIMARY KEY (pkey )
);
ALTER TABLE foo OWNER TO postgres;
Now rename the table:
ALTER table foo RENAME TO bar;
Query returned successfully with no result in 17 ms.
pgAdmin III:
CREATE TABLE bar (
pkey integer NOT NULL DEFAULT nextval('foo_pkey_seq'::regclass),
value integer,
CONSTRAINT foo_pkey PRIMARY KEY (pkey )
);
ALTER TABLE bar OWNER TO postgres;
Note the extra DEFAULT nextval('foo_pkey_seq'::regclass), this means that renaming the table does not rename the sequence for the primary keys but now we have this explicit nextval().
Now rename the sequence:
I want to keep the database naming consistent so I tried:
ALTER SEQUENCE foo_pkey_seq RENAME TO bar_pkey_seq;
Query returned successfully with no result in 17 ms.
pgAdmin III:
CREATE TABLE bar (
pkey serial NOT NULL,
value integer,
CONSTRAINT foo_pkey PRIMARY KEY (pkey )
);
ALTER TABLE bar OWNER TO postgres;
The DEFAULT nextval('foo_pkey_seq'::regclass), is gone.
QUESTIONS
Why did the DEFAULT nextval('foo_pkey_seq'::regclass) statement appear and disappear?
Is there a way to rename the table and have the primary key sequence renamed at the same time?
Is it safe to rename the table then sequence while clients are connected to the database, are there any concurrency issues?
How does postgres know which sequence to use? Is there a database trigger being used internally? Is there anything else to rename other than the table and the sequence?
What about the implicit index created by a primary key? Should that be renamed? If so, how can that be done?
What about the constraint name above? It is still foo_pkey. How is a constraint renamed?
serial is not an actual data type. The manual states:
The data types smallserial, serial and bigserial are not true types,
but merely a notational convenience for creating unique identifier columns
The pseudo data type is resolved doing all of this:
create a sequence named tablename_colname_seq
create the column with type integer (or int2 / int8 respectively for smallserial / bigserial)
make the column NOT NULL DEFAULT nextval('tablename_colname_seq')
make the column own the sequence, so that it gets dropped with it automatically
The system does not know whether you did all this by hand or by way of the pseudo data type serial. pgAdmin checks on the listed features and if all are met, the reverse engineered DDL script is simplified with the matching serial type. If one of the features is not met, this simplification does not take place. That is something pgAdmin does. For the underlying catalog tables it's all the same. There is no serial type as such.
There is no way to automatically rename owned sequences. You can run:
ALTER SEQUENCE ... RENAME TO ...
like you did. The system itself doesn't care about the name. The column DEFAULT stores an OID ('foo_pkey_seq'::regclass), you can change the name of the sequence without breaking that - the OID stays the same. The same goes for foreign keys and similar references inside the database.
The implicit index for the primary key is bound to the name of the PK constraint, which will not change if you change the name of the table. In Postgres 9.2 or later you can use
ALTER TABLE ... RENAME CONSTRAINT ..
to rectify that, too.
There can also be indexes named in reference to the table name. Similar procedure:
ALTER INDEX .. RENAME TO ..
You can have all kinds of informal references to the table name. The system cannot forcibly rename objects that can be named anything you like. And it doesn't care.
Of course you don't want to invalidate SQL code that references those names. Obviously, you don't want to change names while application logic references them. Normally this wouldn't be a problem for names of indexes, sequences or constraints, since those are not normally referenced by name.
Postgres also acquires a lock on objects before renaming them. So if there are concurrent transaction open that have any kind of lock on objects in question, your RENAME operation is stalled until those transactions commit or roll back.
System catalogs and OIDs
The database schema is stored in tables of the system catalog in the system schema pg_catalog. All details in the manual here. If you don't know exactly what you are doing, you shouldn't be messing with those tables at all. One false move and you can break your database. Use the DDL commands Postgres provides.
For some of the most important tables Postgres provides object identifier types and type casts to get the name for the OID and vice versa quickly. Like:
SELECT 'foo_pkey_seq'::regclass
If the schema name is in the search_path and the table name is unique, that gives you the same as:
SELECT oid FROM pg_class WHERE relname = 'foo_pkey_seq';
The primary key of most catalog tables is oid and internally, most references use OIDs.
The SQL for the creation of the table is:
CREATE TABLE myTable(id INTEGER NOT NULL PRIMARY KEY, ...)
Instead I need it to be:
CREATE TABLE myTable(id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), ...)
as described in the Derby documentation. So my question is what would be the alter statement I would need to create AFTER the initial create statement? In other words:
CREATE TABLE myTable(id INTEGER NOT NULL PRIMARY KEY, ...)
ALTER TABLE myTable ...
Thank you very much for the assistance!
Looking at the documentation this seems impossible. You can change the type length (not even the type itself), the default, nullability and the next generated value but even the last option requires the column to already be defined as IDENTITY. A thread from 2009 says that you can't even add an IDENTITY column. A test confirms this is true to this day.
So it seems there is only one solution: You have to replace the table. Something like this:
create a new table with a placeholder name that contains the desired columns
copy any data over from the original table
drop the original table
rename the new table
It's really an unfortunate solution because if you already have other tables referencing the id column of your table as that would mean further work.
I tried messing with the system tables but they seem to be read-only (and for good reason).
Looks like this issue in Derby has been fixed as of the 10.12.1.1 release. Now commands such as:
ALTER TABLE t ADD COLUMN x INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY
to an existing database now work, as does GENERATED BY DEFAULT. Looks like the change requires the underlying database to be at least in 10.11 format.
One technique is to: (a) create a new table with the new column defined as you desire, and all other columns as they were before, (b) run an INSERT INTO ... SELECT ... statement to copy all the data from the existing table to the new table, (c) RENAME TABLE to rename the old table to some other name, (d) RENAME TABLE to rename the new table to the correct tablename, and then finally (e) DROP TABLE the old table.
I have a bunch of tables using user-defined data type for PK column. Is it possible to change this type Using SQL Server 2005?
I would suggest that it is always possible to refactor poor or outmoded database designs, it simply depends on how much work you are willing to go to in order to do so.
If you are looking to replace the user-defined data with a surrogate key then you should be able to simply alter the existing table to contain a non-nullable identity column and this should cause all of the existing records to be assigned a new key automatically.
Once the new field is populated with unique id's, if you need to move out and replace foreign key references to this table, then I would simply alter those tables to contain the new field and use something like the following:
UPDATE child_table
SET new_fk_val =
SELECT new_pk_val
FROM parent_table
WHERE parent_table.old_pk_val = child_table.old_fk_val
Once that step is complete, then you could drop the old foreign key constraint, drop the old foreign key column, drop the old primary key column, establish the new primary key constraint, and then establish the new foreign key constraint.
Of course, if the old version of the parent and child tables relationship was such that you have invalid records in the child table you may have to do something like the following:
DELETE FROM child_table
WHERE old_fk_val NOT IN
( SELECT old_pk_val FROM parent_table)