I am moving from Teradata to Oracle and I have a question regarding Primary Keys and Indexes in the DDLs.
Here are several scenarios and what I am doing:
1) In Teradata there is:
Primary Key("X","Y")
then in Exadata I use:
constraint "PK" PRIMARY KEY ("X","Y")
USING INDEX TABLESPACE DB_NAME" ENABLE;
2) In Teradata there is no primary key but there is:
PRIMARY INDEX ("X")
then in Exadata I use:
CREATE INDEX "DB_NAME"."INDEX_NAME" ON "DB_NAME"."TABLE_NAME" ("X")
3) In Teradata there is no primary key but there is:
UNIQUE PRIMARY INDEX ("X")
then in Exadata I use
constraint "PK" PRIMARY KEY ("X","Y")
USING INDEX TABLESPACE DB_NAME" ENABLE;
4) In Teradata there is no primary key but there is:
INDEX ("X")
then in Exadata I use Create Index
As you can see, It's not entirely clear when I need to use just the Constraint Primary Key and when I need to use create index. Any help would be much appreciated.
Related
In SQL Server, can a table have primary key on 1 column and then composite keys on other two or more columns?
Yes.
A table can have more than one Key, and a Key has one or more key columns.
In SQL Server you create a Key with any of a UNIQUE CONSTRAINT, a PRIMARY KEY CONSTRAINT, or a UNIQUE INDEX. A table can at most one PRIMARY KEY CONSTRAINT, but can have any number of UNIQUE CONSTRAINTs or UNIQUE INDEXs.
So yes, a table can have a PRIMARY KEY on one column, and a composite UNIQUE INDEX or UNIQUE CONSTRAINT.
I want to change the clustered index on a table to a column combo other than the primary key.
How can I drop the clustered index, keep the same primary key I have, and add a new clustered index on a new set of columns.
Final result - Non-clustered indexed PK, clustered index on new set of columns.
The only way you can do this is:
1) First Drop the constraints with the below code:
ALTER TABLE TABLENAME DROP CONSTRAINT ConstrainName
2) Then Create the Clustered Index on the Column you want with the below code:
Create clustered index Index_Name on TableName (column1,column2)
3) Create a primary key on a column :
ALTER TABLE TableName
ADD CONSTRAINT constraint_name PRIMARY KEY (ColumnName);
The "Create Table" grammar rather clearly does not allow me to specify a clustered foreign key constraint. In other words, this is illegal:
--keyword CLUSTERED must be removed before this will execute...
CREATE TABLE [Content](
[ID] [int] NOT NULL CONSTRAINT PK_Content_ID PRIMARY KEY,
ContentDefID int NOT NULL CONSTRAINT FK_Plugin_ContentDef FOREIGN KEY CLUSTERED REFERENCES ContentDef(ID)
)
GO
But I don't understand why it is illegal. ISTM that clustering a foreign-key would facilitate performance of paged-lookups. In other words, "give me child items 80 through 140 of parent ID 20".
Is there a rationale for this?
Update
Based on Oded and Tvanfosson feedback, I've found that the following works:
CREATE TABLE [Content](
[ID] [int] NOT NULL CONSTRAINT PK_Content_ID PRIMARY KEY,
ContentDefID int NOT NULL UNIQUE CLUSTERED CONSTRAINT FK_ContentDefContent FOREIGN KEY REFERENCES ContentDef(ID)
)
GO
But the above causes more problems than it solves. First, a "UNIQUE" foreign key forces my relationship to be one-to-one which I don't want. Second, this only works because it represents the creation of two separate constraints, rather than a single CLUSTERED FOREIGN KEY.
But this investigation is getting me closer to my answer. Evidently clustered indexes MUST be unique, as stated here on SO. Quoting:
If the clustered index is not a unique index, SQL Server makes any duplicate keys unique by adding an internally generated value called a uniqueifier
In particular, I think this answer covers it.
As others have explained, the clustered index does not have to be the primary key but it either has to be unique or SQL-Server adds a (not shown) UNIQUIFIER column to it.
To avoid this, you can make the clustered index unique by explicitly adding the primary key column to the clustered index, like below. The index will then be avaialbel to be used by the foreign key constraints (and for queries, like joining the two tables).
Notice, that as #Martin Smith has explained, the concepts of CONSTRAINT and INDEX are different. And the various DBMSs implement these in different ways. SQL-Server automatically creates an index for some constraints, while it doesn't for foreign key constraints. It's advised though to have an index that the constraint can use (when deleting or updating in the referenced table):
CREATE TABLE Content(
ID int NOT NULL,
ContentDefID int NOT NULL,
CONSTRAINT PK_Content_ID
PRIMARY KEY NONCLUSTERED (ID),
CONSTRAINT CI_Content
UNIQUE CLUSTERED (ContentDefID, ID),
CONSTRAINT FK_Plugin_ContentDef
FOREIGN KEY (ContentDefID) REFERENCES ContentDef(ID)
) ;
Is there a rationale for this?
You might as well ask why you can't create a CLUSTERED check constraint or a CLUSTERED default constraint.
A foreign key simply defines a logical constraint and has no indexes automatically created for it in SQL Server (this only happens for UNIQUE or PRIMARY KEY constraints). It is always the case in SQL Server that if you want the FK columns indexed you need to run a CREATE INDEX on the relevant column(s) yourself.
Therefore the concept of a CLUSTERED FOREIGN KEY doesn't make any sense. You can of course create a CLUSTERED INDEX on the columns making up the FK though as you indicate in your question.
You can only have one clustered index on a table. By default this will be the primary key column.
There are ways to change this - you will need to use PRIMARY KEY NONCLUSTERED and UNIQUE CLUSTERED FOREIGN KEY.
It seems you're conflating the ideas of the clustered index with keys (either primary or foreign). Why not just make the table and then specify its clustered index afterwards? (code copied from your first example and changed as little as possible)
CREATE TABLE [Content](
[ID] [int] NOT NULL CONSTRAINT PK_Content_ID PRIMARY KEY NONCLUSTERED,
ContentDefID int NOT NULL CONSTRAINT FK_Plugin_ContentDef FOREIGN KEY REFERENCES ContentDef(ID)
)
GO
CREATE CLUSTERED INDEX IX_Content_Clustered on Content(ContentDefID)
There's no need for you to make the clustered index unique
Is it possible to create a clustered index from a create table statement in SQL Server 2008 that is not a primary key?
The purpose of this is for a table in SQL Azure, so it is not an option for me to first create the table, and then create the clustered index on the table.
Edit: Apparently it was FluentMigrator that was causing my problems, it's version table does not have a clustered index so it was erroring trying to create the versioning table not my table.
Yes, it is possible to create a clustered index that is not the primary key. Just use a CREATE CLUSTERED INDEX statement.
CREATE TABLE dbo.myTable (
myTableId int PRIMARY KEY NONCLUSTERED
myColumn int NOT NULL
)
CREATE CLUSTERED INDEX myIndex ON dbo.myTable(myColumn)
Prior to version Azure SQL Database v12, you had to have a clustered index before you could insert any data to a table. As of Azure SQL Database v12, heaps (tables without a clustered index) are now supported.
If your database was created prior to June 2016, here are the instructions for upgrading to version 12.
CREATE TABLE dbo.Table_1
(
Id int NOT NULL IDENTITY (1, 1) PRIMARY KEY NONCLUSTERED,
SomeOtherUniqueColumn int NOT NULL CONSTRAINT Item4 UNIQUE CLUSTERED
) ON [PRIMARY]
note the specification of nonclustered on the primary key
This will still work.
CREATE TABLE dbo.Table_1
(
SomeOtherUniqueColumn int NOT NULL CONSTRAINT Item4 UNIQUE CLUSTERED
) ON [PRIMARY]
The code below is compatible with Azure. It creates a primary key non-clustered and a clustered index in a single create table statement. This syntax also allows for specifying more than one column in your key.
CREATE TABLE MyTable (
ID uniqueidentifier NOT NULL,
UserID uniqueidentifier NOT NULL,
EntryDate DATETIME NOT NULL,
CONSTRAINT PK_MyPrimaryKey_Name PRIMARY KEY NONCLUSTERED (ID),
CONSTRAINT UCI_MyClusteredIndexName UNIQUE CLUSTERED (UserID ASC,EntryDate ASC,ID ASC)
);
In order to change a tables clustered index, the clusteredd index must be dropped, which converts the table into a heap and then the new clustered index is applied. Because Azure does not support heaps (tables without clustered indexes) it is not possible to change the clustered index without dropping the table and recreating it. In Azure you can not specify a clustered index in any other place other than the table create statement.
How do I alter a primary clustered index to become a non-clustured index. (Being a "secondary" table I want to use the clustured index for the foreign key column of the "header" table.)
This doen't work for me (error seems reasonable :)
DROP INDEX ClientUsers.PK_ClientUsers
CREATE UNIQUE CLUSTERED INDEX IDX_ClientUsers_Id ON ClientUsers(Id)
Msg 3723, Level 16, State 4, Line 7
An explicit DROP INDEX is not allowed on index 'ClientUsers.PK_ClientUsers'.
It is being used for PRIMARY KEY constraint enforcement.
I think you will have to:
Drop the FK
Drop the PK
Drop the clustered index
Recreate the PK
Recreate the FK
Recreate the clustered index on the FK column
and then optionally create a secondry index on the PK column
Have you tried dropping the PRIMARY KEY constraint on the table as well - then dropping the index - then re-adding both?
ALTER TABLE table_name DROP primary key