I have a table in SQL Server containing some user related info where the primary key is id (auto increment by 1) and has a column named userId. Each user can only has one record in the table, so I have added a unique constraint on column userId. As per SQL Server docs, SQL Server will automatically create an index for the unique constraint column.
For the usage on the table, there can be many update and insert operations, as well as select operations, and that's where my questions arise.
I see that the index that got created automatically by SQL Server on the unique constraint column is a non-clustered index, where it is good for update and insert operations, but for select operation, it is not as fast as the clustered index. (ref. differences-between-a-clustered-and-a-non-clustered-index)
For this table, there can be many select by userId operations. From the performance perspective, should a clustered index on userId be created, given that clustered index is the fastest for read operations ?
If yes, but a non-clustered index has already been automatically created on column userId, could a clustered index still be created on the userId column? (I have found some similar question, from the answers, it seem like if doing so, it will first search through the non-clustered index, then it will points to the clustered index and continue that search non-clustered-index-and-clustered-index-on-the-same-column)
Assuming your table was created in the following manner:
CREATE TABLE dbo.users
(
id int identity(1,1),
userId int,
userName varchar(100),
emailAddress varchar(100),
constraint PK_dbo_users primary key (Id)
);
alter table dbo.users
add constraint UNQ_dbo_users_userId UNIQUE(userId);
... then you already have a clustered index on "id" column by default.
A table can only have one clustered index, as Jonathon Willcock mentioned in the comments. So you cannot add another clustered index to userId column.
You also cannot recreate the clustered index to switch it to the userId column, as the constraints must much the existing constraint. Also, assuming there are foreign key references involved from other tables, you would have to drop the foreign keys before you can drop the users table.
Another option is to create a nonclustered covering index with an INCLUDE clause that contains all the columns needed for your query. This will avoid key lookups in the query plan.
For example:
create nonclustered index IX_dbo_users
on dbo.users (userId) include (id, userName, emailAddress);
Whether the PK and/or clustered index should be on userId or Id column depends on your users queries. If more queries, or more important queries, rely on "id" having clustered index, then keep it. Etc.
But if your table does not already have a clustered index, then yes, add it on userId column.
Related
Does temp table with PK and identity creates clustered index internally in sql server. Or we have to create explicitly?
I was working on SP optimization, and came across one article which says that #Temp table with bulk data can have temp db out of memory issue. i reduced #Table size to only few required columns which stores only int columns. Now considering sort operation, for actual table has some benefit of PK and Clusterd Index. so i was curious does #table utilize the same capabilities of SQL.
IDENTITY is purely a property of a column, like NOT NULL. It isn't CLUSTERED, nor is it a PRIMARY KEY unless you tell SQL Server so.
CREATE TABLE T (ID int IDENTITY); will not create an index on the column ID, nor will be be indexed.
The only way you would create a CLUSTERED INDEX on an IDENTITY column would be by using synnax for declare it as your CLUSTERED PRIMARY KEY in your create, altering your table to add the IDENTITY column as a CLUSTERED PRIMARY KEY or creating a CLUSTERED INDEX on the column (as your primary key and Clustered Index don't have to be the same column).
Could someone simplify the explanation of adding a UNIQUE CONSTRAINT to a column please.
When creating the key index does SQL SERVER copy ALL of the information in the row and add it to the index or just the data in the column with the applied UNIQUE CONSTRAINT?
I hope I explained that properly.
Any help will be greatly appreciated.
Lee.
EDIT**
Ok i think i get it?
CREATE TABLE dbo.test
(
Id int NOT NULL,
Name char(10) NOT NULL UNIQUE
);
INSERT INTO dbo.test (id, name) VALUES (1, 'Lee')
INSERT INTO dbo.test (id, name) VALUES (2, 'Paul')
INSERT INTO dbo.test (id, name) VALUES (3, 'Adam')
INSERT INTO dbo.test (id, name) VALUES (4, 'Henry')
In a clustered index the whole table would be sorted like
3, Adam
4, Henry
1, Lee
2, Paul
So with each additional INSERT the server would have to re-sort the entire table based on the name column?
In a nonclustered index there is another "table" that stores the sort?
When creating the key index does SQL SERVER copy ALL of the
information in the row and add it to the index or just the data in the
column with the applied UNIQUE CONSTRAINT?
There is no such a term as "key index".
Indexes can be clustered or non-clustered.
When you declare UNIQUE CONSTRAINT it's logical entity, but it's physically supported by unique index creation
When you create your unique constraint declaring it as clustered, clustered index will be created. If you don't mention clustered in your constraint definition or use explicite nonclustered, non-clustered index will be created.
Non-clustered index is a separate data structure where every row contains key columns.
On the other hand, clustered index (or better call it clustered table) is data itself + searching B-tree above it. In this case no separate structure is created, it's table itself that now is organized not as a heap but as ordered index.
UNIQUE CONSTRAINT will work just as UNIQUE INDEX. There are 2 ways:
With a clustered index the rows are stored physically on the disk in the same order as the index. (hence, only one clustered index is possible)
With a non clustered index there is a second list that has pointers to the physical rows. You can have many non clustered indexes, although each new index will increase the time it takes to write new records.
If you have both clustered and non clustered index, then non clustered index will point to the clustered index column.
THIS 'SO' answer will help you understand it a bit clear.
By default the unique constraint and Unique index will create a non
clustered index if you don't specify any different (and the PK will by
default be created as CLUSTERED if no conflicting clustered index
exists) but you can explicitly specify CLUSTERED/NONCLUSTERED for any
of them.
Given the database table:
UserID (PK)
SomeTypeID (PK)
SomeSubTypeID (PK)
Data
And you wish to query:
SELECT Data FROM Table WHERE UserID = {0} AND SomeTypeID = {1} AND SomeSubTypeID = {2}
Would you need to create the index UserID, SomeTypeID, SomeSubTypeID or does the fact they form the primary key mean this is not needed?
If you created your primary key as:
CREATE TABLE TBL (UserID, SomeTypeID, SomeSubType, Data
CONSTRAINT PK PRIMARY KEY (UserID, SomeTypeID, SomeSubType))
Then the default index that is being created is a CLUSTERED index.
Usually (so not all times), when looking for data, you would want your queries to use a NON-CLUSTERED index to filter rows, where the columns you use to filter rows will form the key of the index and the information (column) that you return from those rows as an INCLUDED column, in this case DATA, like below:
CREATE NONCLUSTERED INDEX ncl_indx
ON TBL (UserID, SomeTypeID, SomeSubType) INCLUDE (Data);
By doing this, you're avoiding accessing the table data, through the CLUSTERED index.
But, you can specify the type of index that you want your PRIMARY KEY to be, so:
CREATE TABLE TBL (UserID, SomeTypeID, SomeSubType, Data
CONSTRAINT PK PRIMARY KEY NONCLUSTERED (UserID, SomeTypeID, SomeSubType));
Buuut, because you want this to be defined as a PRIMARY KEY then you are not able to use the INCLUDE functionality, so you can't avoid the disk lookup in order to get the information from the DATA column, which is where you basically are with having the default CLUSTERED index.
Buuuuuut, there's still a way to ensure the uniqueness that the Primary Key gives you and benefit from the INCLUDE functionality, so as to do as fewer disk I/O's.
You can specify your NONCLUSTERED INDEX as UNIQUE which will ensure that all of your 3 columns that make up the index key are unique.
CREATE UNIQUE NONCLUSTERED INDEX ncl_indx
ON TBL (UserID, SomeTypeID, SomeSubType) INCLUDE (Data);
By doing all of these then your table is going to be a HEAP, which is not a very good thing. If you've given it a good thought in designing your tables and decided that the best clustering key for your CLUSTERED INDEX is (UserID, SomeTypeID, SomeSubType), then it's best to leave everything as you currently have it.
Otherwise, if you have decided on a different clustering key then you can add this unique nonclustered index, if you're going to query the table as you said you will.
AS long as you use all the columns used in your primary key when filtering you don't need to create seperate indexes. Your primary key is ok in your example.
Think of creating seperate index if you plan to filter on one of the columns and not the others. For example: SELECT Data FROM Table WHERE UserID = {0}
I have created the below table with primary key:
create table test2
(id int primary key,name varchar(20))
insert into test2 values
(1,'mahesh'),(2,'ram'),(3,'sham')
then created the non clustered index on it.
create nonclustered index ind_non_name on test2(name)
when I write below query it will always you non clustered indexes in query execution plan.
select COUNT(*) from test2
select id from test2
select * from test2
Could you please help me to understand why it always use non clustered index even if we have clustered index on table?
Thanks in advance.
Basically when you create a non-clustered index on name, the index actually contains name and id, so it kind of contains all the table itself.
If you add another field like this:
create table test4
(id int primary key clustered,name varchar(20), name2 varchar(20))
insert into test4 values
(1,'mahesh','mahesh'),(2,'ram','mahesh'),(3,'sham','mahesh')
create nonclustered index ind_non_name on test4(name)
You'll see that some of the queries will start using the clustered index.
In your case the indexes are pretty much the same thing, since clustered index also contains the data, your clustered index is id, name and non clustered indexes contain the clustering key, so the non-clustered index is name, id.
You don't have any search criteria, so no matter which index is used, it must be scanned completely anyhow, so why should it actually use the clustered index?
If you add third field you your table, then at least select * will use clustered index.
You are confusing Primary Keys with clustering keys. They are not the same. You will need to explicitly create the clustering key.
To create the clustering key on the primary key in the create statement:
create table test2
(id int ,name varchar(20)
constraint PK_ID_test2 primary key clustered(id))
To add the clustering key to what you have already:
ALTER TABLE test2
ADD CONSTRAINT PK_ID_test2 primary key clustered(id)
I am listing all FK constraints for a given table using INFORMATION_SCHEMA set of views with the following query:
SELECT X.UNIQUE_CONSTRAINT_NAME,
"C".*, "X".*
FROM "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" AS "C"
INNER JOIN "INFORMATION_SCHEMA"."REFERENTIAL_CONSTRAINTS" AS "X"
ON "C"."CONSTRAINT_NAME" = "X"."CONSTRAINT_NAME"
AND "C"."TABLE_NAME" = 'MY_TABLE'
AND "C"."TABLE_SCHEMA" = 'MY_SCHEMA'
Everything works perfectly well, but for one particular constraint the value of UNIQUE_CONSTRAINT_NAME column is wrong, and I need it in order to find additional information from the referenced Column. Basically, for most of the rows the UNIQUE_CONSTRAINT_NAME contains the name of the unique constraint (or PK) in the referenced table, but for one particular FK it is the name of some other unique constraint.
I dropped and re-created the FK - did not help.
My assumption is that the meta-data is somehow screwed. Is there a way to rebuild the meta data so that the INFORMATION_SCHEMA views would actually show the correct data?
edit-1: sample db structure
CREATE TABLE MY_PARENT_TABLE (
ID INTEGER,
NAME VARCHAR,
--//...
CONSTRAINT MY_PARENT_TABLE_PK PRIMARY KEY CLUSTERED (ID)
)
CREATE UNIQUE NONCLUSTERED INDEX MY_PARENT_TABLE_u_nci_ID_LongName ON MY_PARENT_TABLE (ID ASC) INCLUDE (SOME_OTHER_COLUMN)
CREATE TABLE MY_CHILD_TABLE (
ID INTEGER,
PID INTEGER,
NAME VARCHAR,
CONSTRAINT MY_CHILD_TABLE_PK PRIMARY KEY CLUSTERED (ID)
,CONSTRAINT MY_CHILD_TABLE__MY_PARENT_TABLE__FK
FOREIGN KEY (PID)
REFERENCES MY_PARENT_TABLE (ID)
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
I expect the UNIQUE_CONSTRAINT_NAME to be MY_PARENT_TABLE_PK, but what I am
getting is MY_PARENT_TABLE_u_nci_ID_LongName.
Having looked at the structure, I see that in fact there are 2 UNIQUE constaints on that column - PK and the MY_PARENT_TABLE_u_nci_ID_LongName. So the real question should probably be: why does it take some other unique index and not the PK?
Since you have both a PK and a UNIQUE constraint on the same column, SQL Server picks one to use. I don't know if it picks the UNIQUE constraint because it is thinner (i.e. fewer columns involved) and might require fewer reads to confirm matches(?)
I don't see any way within SQL to enforce which one it chooses, other than ordering your scripts - create the table with the PK, create the other table and the FK, then create the UNIQUE constraint if you really need it - but is that really the case?