I have a table containing an INT IDENTITY column, and a VARCHAR(10) column. I want to have a UNIQUE CLUSTERED INDEX on the IDENTITY column (so it's a primary key), and I also want to have a way of preventing duplicate values in the VARCHAR(10) column. However, the data should be ordered by the IDENTITY column in ascending order.
For example:
CREATE TABLE ref.currency (
currency_id INT NOT NULL IDENTITY(1,1),
currency_name VARCHAR(10) NOT NULL
);
GO
CREATE UNIQUE CLUSTERED INDEX ix_ref_currency_id ON ref.currency(currency_id);
GO
INSERT INTO ref.currency (
currency_name
)
VALUES
('Pounds'),
('Euros'),
('Dollars');
(I create a UNIQUE CLUSTERED INDEX instead of a PRIMARY KEY, as I was taught this, but didnt fully understand the reasons why one should be chosen over the other. I've stuck with it ever since.)
Question:
What type of index should I add to this table in order to prevent duplicate values being added to the currency_name column, that will not affect the order of the data?
I've tried adding a UNQIUE NONCLUSTERED INDEX, however this results in the data being ordered by currency_name, which I do not want.
Related
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)
How do I switch off the default index on primary keys
I dont want all my tables to be indexed (sorted) but they must have a primary key
You can define a primary key index as NONCLUSTERED to prevent the table rows from being ordered according to the primary key, but you cannot define a primary key without some associated index.
Tables are always unsorted - there is no "default" order for a table and the optimiser may or may not choose to use an index if one exists.
In SQL Server an index is effectively the only way to implement a key. You get a choice between clustered or nonclustered indexes - that is all.
The means by which SQL Server implements Primary and Unique keys is by placing an index on those columns. So you cannot have a Primary Key (or Unique constraint) without an index.
You can tell SQL Server to use a nonclustered index to implement these indexes. If there are only nonclustered indexes on a table (or no indexes at all), you have a heap. It's pretty rare that this is what you actually want.
Just because a table has a clustered index, this in no way indicates that the rows of the table will be returned in the "order" defined by such an index - the fact that the rows are usually returned in that order is an implementation quirk.
And the actual code would be:
CREATE TABLE T (
Column1 char(1) not null,
Column2 char(1) not null,
Column3 char(1) not null,
constraint PK_T PRIMARY KEY NONCLUSTERED (Column2,Column3)
)
What does " I dont want all my tables to be sorted" mean ? If it means that you want the rows to appear in the order where they've been entered, there's only one way to garantee it: have a field that stores that order (or the time if you don't have a lot of transactions). And in that case, you will want to have a clustered index on that field for best performance.
You might end up with a non clustered PK (like the productId) AND a clustered unique index on your autonumber_or_timestamp field for max performance.
But that's really depending on the reality your're trying to model, and your question contains too little information about this. DB design is NOT abstract thinking.
Is the following possible? I am unable to do so. Do I have to have a permanent table to create index?
declare #Beatles table
(
LastName varchar(20) ,
FirstName varchar(20)
)
CREATE CLUSTERED INDEX Index_Name_Clstd ON #Beatles(LastName)
Not on a table variable, but on a temp table see this http://www.sqlteam.com/article/optimizing-performance-indexes-on-temp-tables
No, you cannot create indices on a table variable - see this article here and this posting here comparing local, global temporary tables to table variables.
Restrictions
You cannot create a non-clustered
index on a table variable, unless the
index is a side effect of a PRIMARY
KEY or UNIQUE constraint on the table
(SQL Server enforces any UNIQUE or
PRIMARY KEY constraints using an
index).
According to this post - YES you can.
The following declaration will generate 2 indexes:
DECLARE #Users TABLE
(
UserID INT PRIMARY KEY,
UserName VARCHAR(50),
FirstName VARCHAR(50),
UNIQUE (UserName,UserID)
)
The first index will be clustered, and will include the primary key.
The second index will be non clustered and will include the the columns listed in the unique constraint.
Here is another post, showing how to force the query optimizer to use the indexes generated dynamically, because it will tend to ignore them (the indexes will be generated after the execution plan is evaluated)