In SQL can a table have both primary keys + composite keys? - sql-server

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.

Related

How to add another primary key in SQL Server

I have a SQL Server table that has primary keys
CompanyID
ClientID
ReportName
I need to add a column that is an additional key and an incremented ID.
alter table Exports
add id int identity(1,1)
How do I write the statement to make this a key as well?
You can have only 1 primary key or identity column in each table in SQL Server.
If you want to have one more column as primary key, then you may use the combination of the columns as the primary key.
Or you can set the constraints UNIQUE and NOT NULL to the columns so that they will act same as a primary key (but these columns can't be referred as foreign keys)
In case of auto incremental columns (identity), you can only have 1 identity column per table. It can also be set as a primary key. Otherwise, if you need multiple columns as auto-increment, then probably you can use a calculated column, a trigger or a sequence object from which you may fetch the values for each record

What is the impact of creating a table with a unique index but no primary key?

What is the best way to make a simple many-to-many cross reference table which contains nothing but two columns which are themselves primary keys in other tables?
Does anyone have concrete evidence for or against creating a table with a single unique index, but no primary key? (Alternatives are detailed below).
Put another way: How does SQL Server internally uniquely identifies rows a) that have a primary key and b) that do not have a primary key?
In detail:
Given the input tables:
CREATE TABLE Foo ( FooID bigint identity(1,1) not null primary key, other stuff... )
CREATE TABLE Bar ( BarID bigint identity(1,1) not null primary key, other stuff... )
The three basic options are (in all cases assume a foreign key is created on the FooID and BarID columns):
-- Option 1: Compound primary key
CREATE TABLE FooBarXRef (
FooID bigint not null
, BarID bigint not null
, PRIMARY KEY ( FooID, BarID )
, CONSTRAINT FK... etc
)
-- Option 2: Independent primary key + unique index
CREATE TABLE FooBarXRef (
FooBarXRefID bigint identity(1,1) not null primary key
, FooID bigint not null
, BarID bigint not null
, CONSTRAINT FK... etc
);
CREATE UNIQUE INDEX I_FooBarXRef_FooBar ON FooBarXRef ( FooID, BarID );
-- Option 3: Unique index, no explicit primary key:
CREATE TABLE FooBarXRef (
FooID bigint not null
, BarID bigint not null
, CONSTRAINT FK... etc
);
CREATE UNIQUE INDEX I_FooBarXRef_FooBar ON FooBarXRef ( FooID, BarID );
Does having a separate identity PK on the xref table to be redundant; that may needlessly introduces another layer of constraint checking on the database engine?
On the other hand are multi-column primary keys problematic? With a proposed solution to have the xref table contain only the two foreign keys, and define a unique index on those columns, but not define a primary key at all... ?
I suspect that doing so will cause SQL Server to create an internal primary key for the purposes of uniquely identifying each row, thus yielding the same redundant constraints as if a primary key were defined explicitly--but I have no proof or documentation to support this. Other questions and answers suggest that there is not an internal primary key by default (i.e. no equivalent to the Oracle ROWID); as the %%physloc%% is an indicator of where a row is currently stored and thus is subject to change. My intuition is that the engine must create something to uniquely identify a row in order to implement cursors, transactions, and concurrency.
The concept of a primary key is really about relational theory; maintaining referential integrity by building relationships across multiple tables. The SQL Server engine, by default, creates a unique clustered index when a primary key is built (assuming a clustered index doesn't exist at the moment).
It's this clustered index that defines a unique row at the leaf level. For tables that have a non-unique clustered index, SQL Server creates a 4byte "uniquifier" to to the end of your key.
TestTable1 Primary Key
TestTable2 Primary Key & Unique Non-Clustered
TestTable3 Unique Clustered
TestTable4 Primary Clustered (same as Table1 & Table3, since a primary key CAN be defined on a non-clustered index I prefer this to always define which structure I want).
TestTable2 is redundant, it's create a unique clustered index to store all the records at it's leaf level. It's then creating a unique non-clustered index to enforce uniqueness once again. Any changes on the table will hit the clustered and then the non-cluster.
TestTable1, TestTable3, TestTable4 are a tie in my book, a unique clustered index structure is created on all. There is no physical difference in the way records are stored on a page.
However for SQL Server Replication, all replicated tables required a primary key. If your'll be using Replication in the future you may want to make sure all your unique clustered indexes are primary keys as well.
I seem to be unable to paste in my verifying scripts, so here they are on hastebin.
http://hastebin.com/qucajimixi.vbs
Well, it all depends on the requirement. As far as I know
PRIMARY KEY= UNIQUE KEY+NOT NULL key
What this tells you is that you can have multiple
NOT NULL UNIQUE INDEXES(NON CLUSTERED)
but
CANNOT HAVE MULTIPLE PRIMARY KEYS IN A TABLE( CLUSTERED).
I am a huge believer of Relational database model and working with the PRIMARY-FOREIGN KEYS relationships. DB replication requires you to have Primary Key on a table ; therefore, it is always a good practice to create Primary Key instead of UNIQUE keys for your table.

SQL Server Table Partition

I have a Table with the following columns
ID (INT Primary Key)
RecordDate (DateTime non-unique)
Name (varchar)
I have partitioned the table based on field RecordDate (Monthly) to different file groups.
Now, how can I add a primary key ID to this partitioned scheme with out combining field with RecordDate?
The short answer is that your primary key cannot be clustered if you don't want to include your partition column in your primary key as a composite column. So create a clustered index on the partition scheme for RecordDate. Then when you create your primary key constraint, set it to nonclustered.
Please note this can degrade performance and cause memory contention, and is generally not recommended.

T-SQL Clustered Foreign Key

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

Should my intermediate table contain a composite PK or a unique index

Let's say I have a Products table, ProductsCategory table and a Category table.
The ProductsCategory table has two columns: ProductID and CategoryID. Should I be using a composite primary key or a unique index on the two columns?
Additionally if I use the index, I can make it a unique index or a key.
Might as well use a composite key - no need to add a unique index when you already have the uniqueness semantics of a composite primary key.
You must create two way foreign key both table with on delete cascade option.
Because if you delete one of categories then it must remove relational rows on ProductCategory.
i mean you can use like this :
alter table ProductsCategory add constraint ForeignKey1 foreign key (ProductId) references Products (ID) ON DELETE CASCADE;
alter table ProductsCategory add constraint ForeignKey2 foreign key (CategoryId) references Category (ID) ON DELETE CASCADE;

Resources