How can you tell what method a constraint was created with in SQL Server 2008? - sql

There are two ways to create a constraint,
Method A:
ALTER TABLE dbo.<tablename> ADD CONSTRAINT
<namingconventionconstraint> UNIQUE NONCLUSTERED
(
<columnname>
Method B:
CREATE UNIQUE NONCLUSTERED INDEX
<namingconventionconstraint> ON dbo.<tablename>
(
<columnname>
) ON [PRIMARY]
However, it appears that these constraints need to be dropped using a method that is dependent on how they were created (Drop constraint vs drop index). Is there a way to determine what method the constraint was created other than trying a method and seeing if it fails? I know you can have SQL Server create a drop script for you but I'm looking for a query of some sort.

A unique constraint will appear in sys.objects with type = UQ
A unique index will not appear in sys.objects
Both appear in sys.indexes
This is why you can have the same index name for many table, but a table level unique constraint has to be database/schema unique

Related

Replace the default clustered index with a non-primary/non-key attribute

I have a request to replace the default clustered index with a non-primary attribute and I have an idea of how to simply drop an index and just make another one but I have no clue how to replace the default clustered index.
This is as far as I got.
CREATE INDEX newCustomerIndex ON CustomerInfo(fullname, homeAddress);
DROP INDEX CustomerInfo.PK__Customer__B611CB9D24135D51
However whenever I execute this piece of code I get this error
An explicit DROP INDEX is not allowed on index 'CustomerInfo.PK__Customer__B611CB9D24135D51'. It is being used for PRIMARY KEY constraint enforcement.
There are definitely times when you want the Clustered Index to be separate from the Primary Key - particularly when you search in 'ranges'.
For example, on log tables, I'll frequently have the primary key as an Identity (auto-incrementing) integer so each row is uniquely identified, but I'll put the clustered index on the date/time stamp field. This is because most of the reads etc will be within a date range and therefore the clustered index can be used directly. However, you cannot guarantee each date/time stamp is unique so you cannot use it as a primary key - and you can only have 1 clustered index on a table.
Note that the below is based on SQL Server - as per your tags. However, I'm not sure if the fields you are referring to are the current PK, or the ones you want to have as your clustered index. Therefore, I'll write this for a fictional table called 'dbo.Temp' with 'Auto_Id' and 'Log_datetime'.
I assume you already have the primary key which is also the clustered index. The typical process is as follows.
DROP and CREATE the current primary key, but as a non-clustered index rather than clustered
Create your new clustered index
However, you will also need a preceding and following step - identifying and managing the foreign key constraints. I'll get to this last.
To do step 1, I will usually open the table on the left (object explorer), open 'Indexes', then right-click on the index and script index -> 'DROP and CREATE to
This gives some code similar to the following
ALTER TABLE [dbo].[Temp] DROP CONSTRAINT [PK_Temp] WITH ( ONLINE = OFF )
GO
ALTER TABLE [dbo].[Temp] ADD CONSTRAINT [PK_Temp] PRIMARY KEY CLUSTERED
(
[Auto_ID] ASC
)
GO
I make one change to this before starting - change the new constraint to be NONCLUSTERED rather than clustered e.g.,
ALTER TABLE [dbo].[Temp] DROP CONSTRAINT [PK_Temp] WITH ( ONLINE = OFF )
GO
ALTER TABLE [dbo].[Temp] ADD CONSTRAINT [PK_Temp] PRIMARY KEY NONCLUSTERED -- modified here
(
[Auto_ID] ASC
)
GO
I then add my create index statement to create the index e.g.,
CREATE CLUSTERED INDEX CX_Temp ON dbo.Temp ([Log_Datetime], [Auto_ID])
GO
And then run.
Sometimes this won't work - especially when the index is being used for foreign key constraints (e.g., another table has a foreign link to this table/current PK) - therefore you cannot drop the PK, nor any of the following steps.
Therefore you need to find the relevant foreign key constraint. Once found (which can be annoying), I then usually do a similar thing to script the index deletion and re-creation (right-click on the foreign key, script it as 'DROP and CREATE to').
You then copy the first part (dropping the foreign key) before the script above, add the re-creating of the foreign key to the end. This therefore
disables the foreign key constraint
deletes/recreates the primary key as a non-clustered index
creates the new clustered index
recreates the foreign key constraint

Assigning an index to a constraint

I have a script written for Oracle databases that I'm converting to work in SQL Server and I have two questions about a specific section of code.
In Oracle script I have this code:
CREATE UNIQUE INDEX "PK_PORTALROLES" ON "PORTAL_ROLE" ("ROLE_NAME");
ALTER TABLE "PORTAL_ROLE" ADD CONSTRAINT "PK_PORTALROLES"
PRIMARY KEY ("ROLE_NAME") USING INDEX ENABLE;
Question (1)
From the code above what is the USING INDEX command in the ALTER TABLE line doing? Is it assigning the UNIQUE INDEX created on the first line to the newly created CONSTRAINT or is the CONSTRAINT getting a new UNIQUE INDEX to use when it is created?
Question (2)
To duplicate this in SQL Server, I commented out the CREATE UNIQUE INDEX line like this:
--CREATE UNIQUE INDEX "PK_PORTALROLES" ON "PORTAL_ROLE" ("ROLE_NAME");
And then replaced the ALTER TABLE line with this:
ALTER TABLE "PORTAL_ROLE" ADD CONSTRAINT "PK_PORTALROLES" PRIMARY KEY ("ROLE_NAME");
I understand that when a PRIMARY KEY CONSTRAINT is created in SQL Server a UNIQUE INDEX is automatically made. So is the one line of SQL Server code directly above doing the same thing as the two lines of Oracle code above?
EDIT
One final question. Is there a way in Oracle and SQL Server to assign an existing INDEX to a CONSTRAINT?
In Oracle
If there are more than one indexes on the column on which you want to add PK constraint, we can selectively choose the index to be assoicated with the PK using “USING INDEX“. This clause can be used while:
Adding the PK constraint for the first time (using “ALTER TABLE” command).
CREATE TABLE tbl_test ( col_1 NUMBER,
col_2 NUMBER,
col_3 NUMBER);
CREATE INDEX idx_col_1_2 ON tbl_test(col_1, col_2);
CREATE INDEX idx_col_1_3 ON tbl_test(col_1, col_3);
CREATE UNIQUE INDEX idx_col_1 ON tbl_test(col_1);
-- Forcing oracle to use the unique index "IDX_COL_1"
ALTER TABLE tbl_test ADD CONSTRAINT tbl_test_pk PRIMARY KEY(col_1)
USING INDEX idx_col_1;
SELECT constraint_name, constraint_type, index_name
FROM user_constraints
WHERE table_name = 'TBL_TEST';
-- CONSTRAINT_NAME | CONSTRAINT_TYPE | INDEX_NAME
-- TBL_TEST_PK | P | IDX_COL_1
What if? If you don't use the USING INDEX clause
ALTER TABLE tbl_test ADD CONSTRAINT tbl_test_pk PRIMARY KEY(col_1);
-- Although an unique index exists, oracle has picked up the first index
SELECT constraint_name, constraint_type, index_name
FROM user_constraints
WHERE table_name = 'TBL_TEST';
-- CONSTRAINT_NAME | CONSTRAINT_TYPE | INDEX_NAME
-- TBL_TEST_PK | P | IDX_COL_1_2
That shows The index associated with the PK constraint needn’t be unique.
But in SQLServer implicitly CLUSTERED INDEX will be created when primary key defined on any column
So to your last question. In Oracle you can assign index which we created can be assigned to constraints which we will create in future.
In SQLServer i guess it is not possible.
In Oracle, you can do
alter table PORTAL_ROLE add constraint pk_portalroles primary key('ROLE_NAME');
And it will create unique index for you. But when you drop or disable(!) constraint, it will drop that index. To avoid that (or to give index some custom name, or to use non-unique index, etc), it is usually done in 2 steps as in your code (as recommended by ORACLE).
Q1: constraint will be using existing index.
Q2: Yes, it will be enough for SQL Server.
When you specify a unique constraint on one or more columns, Oracle implicitly creates an index on the unique key. If you are defining uniqueness for purposes of query performance, then Oracle recommends that you instead create the unique index explicitly using a CREATE UNIQUE INDEX statement. You can also use the CREATE UNIQUE INDEX statement to create a unique function-based index that defines a conditional unique constraint.

Set identity off in azure sql table

I have a table which includes an identity column but i cant remove the identity property.
Is there a way to disable it? Or a way to make a copy of the entire table without identity property?
Note that you may not be able to drop the column if it referenced by a clustered index, and you can't drop all clustered indexes for a table because SqlAzure tables must always have a clustered index.
This means that you may have to jump through the following hoops (for at least your last clustered index, which may well be your primary key):
rename your clustered index
create a temp version of the table (with a new clustered index)
copy the data from the current table
drop the current table
rename the temp table to the current name
This roughly looks like this:
-- Rename clustered index
EXECUTE sp_rename N'PK_My_Current_PK', N'PK_My_Current_PK_OLD', 'OBJECT'
-- If you have any FK constraints on the table, then drop them
ALTER TABLE dbo.MyTable DROP CONSTRAINT FK_My_Foreign_Key
-- Create the new version of your table - because this is SQLAzure it must have a clustered index
CREATE TABLE dbo.tmp_MyTable (
MyID int NOT NULL,
CONSTRAINT PK_My_Current_PK PRIMARY KEY CLUSTERED (MyID)
)
-- Copy the data into the temp table from the old table
INSERT INTO dbo.tmp_MyTable (MyID)
SELECT MyID FROM dbo.MyTable
-- Drop the old table
DROP TABLE dbo.MyTable
-- Rename the new table
EXECUTE sp_rename N'tmp_MyTable', N'MyTable', 'OBJECT'
-- Recreate any foreign key constraints
ALTER TABLE dbo.MyTable WITH CHECK ADD FK_My_Foreign_Key FOREIGN KEY (MyID)
REFERENCES dbo.MyForeignTable (MyID)
Hope that helps
A
Edit: As #PhilBolduc pointed out SqlAzure tables require a clustered index, not a primary key. I've amended the terminology above accordingly - the principle of the answer still remains.
You can not remove an Identity column without dropping it unfortunately. Alternetivly add a new column with a temp name, update the new column value and then drop the previous column.
ALTER TABLE dbo.tablename ADD newcolumnname INT
UPDATE dbo.tablename SET newcolumnname = oldcolumnname FROM dbo.tablename
ALTER TABLE dbo.tablename DROP COLUMN oldcolumnname
that should do it. unless i have misunderstood your questions?

Drop Default Clustered Index Without Knowing it's Name

I wrote a script that creates some tables (it previously drops them if they exist) and then tries to create two indexes on each table.
The first index uses the Primary Key column to create a non-clustered index, and the second uses another column to create the clustered indexed. This is because the primary key column is a GUID instead of an int.
How can I drop the default index if I don't know it's name? or how can I specify a name for the primary key column index so I can drop it? Or better yet, how can I specify the 2 index i need right in the Create Table statement?
SELECT * FROM sys.indexes
However, I'm not understanding where in your process you actually have to drop an index.
You said you are creating some tables and then creating two indexes on each table.
If you are DROPping existing tables at the beginning, any indexes are automatically dropped.
There is no such thing as a default index.
Tables can either be heaps or clustered indexes. If you drop the clustered index, the table will be converted to a heap and any non-clustered indexes will have to be updated to point to the data in the unordered heap.
You can create like this all at once:
CREATE TABLE dbo.tbl
(
Id int NOT NULL IDENTITY (1, 1) CONSTRAINT UK_ID UNIQUE CLUSTERED,
SomeUUID UNIQUEIDENTIFIER NOT NULL CONSTRAINT PK_SomeUUID PRIMARY KEY NONCLUSTERED
)
Here's a SQLFiddle: http://sqlfiddle.com/#!6/d759e/12
You can define the two indices right after you create the table:
CREATE TABLE dbo.YourTable ( ...... )
GO
ALTER TABLE dbo.YourTable
ADD CONSTRAINT PK_YourTable PRIMARY KEY NONCLUSTERED (YourGuidColumn)
****************
this is crucial ! Otherwise, your PK will be clustered!
CREATE CLUSTERED INDEX IX01_YourTable ON dbo.YourTable(YourOtherColumn)
or even better:
CREATE UNIQUE CLUSTERED INDEX IX01_YourTable ON dbo.YourTable(YourOtherColumn)
That should create a non-clustered primary key and a separate (preferably unique) clustered index on a separate column.

Does making a column unique force an index to be created?

In SQL Server 2005+ (I use both), does adding the UNIQUE constraint to a column automatically create an index, or should I still CREATE INDEX?
See this MSDN article:
The Database Engine automatically
creates a UNIQUE index to enforce the
uniqueness requirement of the UNIQUE
constraint.
If you do create an index, you'll end up with two indexes, as this example demonstrates:
create table TestTable (id int)
alter table TestTable add constraint unique_id unique (id)
create unique index ix_TestTable_id on TestTable (id)
select * from sys.indexes where [object_id] = object_id('TestTable')
This will display two unique indexes on TestTable; and the HEAP that represents the table itself.
Yes, it does.
In fact, you can even create a CLUSTERED UNIQUE CONSTRAINT:
ALTER TABLE mytable ADD CONSTRAINT UX_mytable_col1 UNIQUE CLUSTERED (col1)
, which will make the table to be clustered on col1.
Almost all databases create an index for UNIQUE CONSTRAINT, otherwise it would be very hard to maintain it.
Oracle doesn't even distinguish between UNIQUE CONSTRAINT and UNIQUE INDEX: one command is just a synonym for another.
The only difference in Oracle is that a UNIQUE INDEX should have a user-supplied name, while a UNIQUE CONSTRAINT may be created with a system-generated name:
ALTER TABLE mytable MODIFY col1 UNIQUE
This will create an index called SYS_CXXXXXX.
An index is created when you add a unique constraint:
Reference -- see the second paragraph.
When a UNIQUE constraint is added to
an existing column or columns in the
table, by default, the Database Engine
examines the existing data in the
columns to make sure all values are
unique. If a UNIQUE constraint is
added to a column that has duplicated
values, the Database Engine returns an
error and does not add the constraint.
The Database Engine automatically
creates a UNIQUE index to enforce the
uniqueness requirement of the UNIQUE
constraint. Therefore, if an attempt
to insert a duplicate row is made, the
Database Engine returns an error
message that states the UNIQUE
constraint has been violated and does
not add the row to the table. Unless a
clustered index is explicitly
specified, a unique, nonclustered
index is created by default to enforce
the UNIQUE constraint.