Clustered index on temp table - sql

I'm trying to optimize a procedure that has code like the following:
CREATE TABLE #t1 (c1 int, c2 varchar(20), c3(varchar(50)...)
CREATE CLUSTERED INDEX ix_t1 ON #t1(c3) ON [PRIMARY]
I wanted to improve that by moving the CLUSTERED index into the table declaration (more caching friendly), but c3 is not unique so this doesn't work:
CREATE TABLE #t1 (c1 int, c2 varchar(20), c3 varchar(50)..., UNIQUE CLUSTERED (c3))
Is there a way to declare a clustered that is not unique in the temp table declaration?

Yes, it is possible in SQL Server 2014 and above, Create table on MSDN.
From 2014 you can specify the indexes inline with the create table statement.
if object_id('tempdb..#t1') is not null drop table #t1;
CREATE TABLE #t1 (
c1 int,
c2 varchar(20),
c3 varchar(50),
index [CIX_c3] CLUSTERED (c3),
index [IX_c1] nonclustered (c1)
)
insert #t1(c3) values ('a'), ('a'), ('a')
select * from #t1

No there is not...the existence of the ability to define clustered as an option in table creation is to support declaring primary key and unique column constraints, which themselves create indexes. In other words, CLUSTERED in the CREATE TABLE statement is specifying whether or not the index created by the UNIQUE constraint should be clustered or nonclustered, which is important because a table can only have one clustered index.

This can be done by adding an identity column, such as:
CREATE TABLE #t1 (rowID int not null identity(1,1),
c1 int, c2 varchar(20), c3 varchar(50),
UNIQUE CLUSTERED (c3,rowID)
)
Including the rowID in the index will insure that it is unique, even if c3 is not.
You verify the index created with:
EXEC tempdb.dbo.sp_helpindex '#t1'

Related

Alter table variable to add primary key get error Incorrect syntax

I want add a composite primary key constraint to a defined table variable depending on a condition:
DECLARE #tbl TABLE(Col1 int, Col2 int)
IF [myCondition]
ALTER TABLE #tbl ADD CONSTRAINT c PRIMARY KEY(Col1)
ELSE
ALTER TABLE #tbl ADD CONSTRAINT c PRIMARY KEY(Col1,Col2)
But get:
Incorrect syntax near '#tbl'.
Alter statements can't be used on table variables.
As an alternative, use a temporary table:
CREATE TABLE #tbl (Col1 int not null, Col2 int not null)
IF [myCondition]
ALTER TABLE #tbl ADD CONSTRAINT c PRIMARY KEY(Col1)
ELSE
ALTER TABLE #tbl ADD CONSTRAINT c PRIMARY KEY(Col1,Col2)

Alter datatype of clustered index primary key in table

I have a table with
CONSTRAINT [user_const] PRIMARY KEY CLUSTERED ([id] ASC, [group] ASC)
I want to change the datatype of group to NVARCHAR from NCHAR.
Simply running
ALTER TABLE [dbo].[user] ALTER COLUMN [group] NVARCHAR (50) NOT NULL;
gives me an error:
The object 'user_const' is dependent on column 'group'.
CHECK NOCHECK does not work as
This action applies only to foreign key and check constraints.
So, I thought I should drop the constraint and recreate it
ALTER TABLE [dbo].[user] DROP CONSTRAINT [user_const];
ALTER TABLE [dbo].[user] ALTER COLUMN [group] NVARCHAR (50) NOT NULL;
ALTER TABLE [dbo].[user] ADD CONSTRAINT CONSTRAINT [user_const] PRIMARY KEY CLUSTERED ([id] ASC, [group] ASC)
But error reads
Tables without a clustered index are not supported in this version of SQL Server.
How can I alter the datatype of a column which is a primary key constraint?
Azure does not support tables without clustered indexes. Read here
http://msdn.microsoft.com/library/azure/ee336245.aspx#cir
In order to change the data type, you are going to have to create a second table with the new datatypes, move the data over, and then rename.
Since you're changing the primary key, you're effectively rebuilding the entire table anyway, so you might as well build a new table with the new format and rename to get the name right. I haven't run this so it might need a little tweaking, but something like this:
-- check to see if we've already run this script and swap table names and just run it again with the old data if we have
IF EXISTS (SELECT * FROM SysObjects WHERE name='MyTable_bak' AND type='U')
IF EXISTS (SELECT * FROM SysObjects WHERE name='MyTable' AND type='U')
DROP TABLE MyTable
ELSE
EXEC sp_rename 'MyTable', 'MyTable_bak'
GO
-- create the new table with updated columns
CREATE TABLE MyTable (
[id] BIGINT NOT NULL,
[group] NVARCHAR(50) NOT NULL,
CONSTRAINT PK_MyTable PRIMARY KEY CLUSTERED
(
[id] ASC, [group] ASC
))
GO
-- copy in the data from the old version
INSERT MyTable([id],[group])
SELECT [id],[group]
FROM MyTable_bak
GO
-- drop the old table (maybe wait to do this until testing is complete?)
DROP TABLE MyTable_bak

how to drop an index key from a table

I'm using sql server 2005/2008 and I have a table that it's PK (rec_index) is also indexed.
CREATE TABLE [dbo].[MyTable]
(
[rec_index] INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
file_desc nvarchar(50),
is_modified bit
)
However unfortunately I already have a table like this in my DB which O created without the index.
How can I ask if the table already is indexed do nothing, else add an index? I can't drop it and than re-create it since i will loose data.
I'm looking for something like "if not exists <index for rec_index> do <create index for rec_index>"
maybe something like:
IF NOT EXISTS ( select *
from sys.objects
where parent_object_id = object_id('MyTable')
and type = 'PK' )
do <create index for rec_index>
There are so many possibilities here, and I'm not sure which situation you're hitting, and what you want to do in each case:
USE [tempdb];
-- table with a clustered PK
CREATE TABLE dbo.TableA(rec_index INT IDENTITY(1,1) PRIMARY KEY CLUSTERED);
-- table with non-clustered PK
CREATE TABLE dbo.TableB(rec_index INT IDENTITY(1,1) PRIMARY KEY NONCLUSTERED);
-- table with no PK, no indexes
CREATE TABLE dbo.TableC(rec_index INT IDENTITY(1,1));
-- table with no PK, non-clustered non-unique index
CREATE TABLE dbo.TableD(rec_index INT IDENTITY(1,1));
CREATE INDEX d ON dbo.TableD(rec_index);
-- table with no PK, clustered non-unique index
CREATE TABLE dbo.TableE(rec_index INT IDENTITY(1,1));
CREATE CLUSTERED INDEX e ON dbo.TableE(rec_index);
-- table with no PK, non-clustered unique index
CREATE TABLE dbo.TableF(rec_index INT IDENTITY(1,1));
CREATE UNIQUE INDEX f ON dbo.TableF(rec_index);
-- table with no PK, clustered unique index
CREATE TABLE dbo.TableG(rec_index INT IDENTITY(1,1));
CREATE UNIQUE CLUSTERED INDEX g ON dbo.TableG(rec_index);
-- table with unique clustered index, but PK on different column
CREATE TABLE dbo.TableH(rec_index INT IDENTITY(1,1),
b INT PRIMARY KEY NONCLUSTERED);
CREATE UNIQUE CLUSTERED INDEX h ON dbo.TableH(rec_index);
GO
DROP TABLE dbo.TableA,dbo.TableB,dbo.TableC,dbo.TableD,
dbo.TableE,dbo.TableF,dbo.TableG,dbo.TableH;
GO
You can certainly perform checks for each of these scenarios from the system metadata, and react accordingly. But you need to help us narrow down what you mean by "without the index."

How would I create an index on this temp table?

I am trying to speed up a query and I think I am confused about indexes. How, and what index would I add to this table. The ID is Unique, would this be a primary index?
CREATE TABLE #OSP
(
[Id] UniqueIdentifier,
[YearMonth] int,
[Expenditure] decimal (7,2),
[Permit] decimal (7,2)
);
You can specify the primary key in your create table statement.
CREATE TABLE #OSP
(
[Id] UniqueIdentifier primary key,
[YearMonth] int,
[Expenditure] decimal (7,2),
[Permit] decimal (7,2)
);
If you're joining on id then creating an index on that would help.
I think this would work:
CREATE TABLE #OSP
(
[Id] UniqueIdentifier,
[YearMonth] int,
[Expenditure] decimal (7,2),
[Permit] decimal (7,2)
);
CREATE UNIQUE CLUSTERED INDEX [idx_id] ON #Osp ([Id] ASC)

SQL Server add primary key

I have a table that needs to be given a new primary key, as my predecesor used a varchar(8) row as the primary key, and we are having problems with it now. I know how to add the primary key, but am not sure of the correct way to add this new primary key to other tables that have the foreign key. Here is what I have:
users table:
old_user_id varchar(8)
...
...
new_user_id int
orders table:
order_id int
...
...
old_user_fk varchar(8)
new_user_fk int(11)
I need to get the same results whether I join the tables on users.old_user_id=orders.old_user_fk or users.new_user_id=orders.new_user_fk. Any help is appreciated.
What is int(11)? SQL Server only has tinyint, smallint, int and bigint
I would suggest you add the new columns to all the tables then update the values so that they match....run a couple of queries to make sure it all works. drop the PK and FK constraints and add new PK and FK constraints using the new columns
of course I would back up all these tables just in case
select * into backup_ orders from orders
This way you always have that data in case you need to roll back
Replace your varchar with int, do not keep duplicates in the same table.
Write some TSQL similar this code I used recently:
BEGIN TRANSACTION
-- temp tables to hold data
DECLARE #HeaderTemp table
(vid varchar(8),
[Name] varchar (50) )
DECLARE #SecondaryTemp table
(vid_fk varchar(8),
[Value] varchar (50) )
-- store table data
INSERT INTO #HeaderTemp
SELECT * FROM [Header]
INSERT INTO #SecondaryTemp
SELECT * FROM [Secondary]
-- empty data from tables
DELETE FROM [Secondary]
DELETE FROM [Header]
-- drop constraints
ALTER TABLE [SECONDARY] DROP CONSTRAINT [FK_SECONDARY_HEADER]
ALTER TABLE [dbo].[HEADER] DROP CONSTRAINT [PK_HEADER]
-- convert varchar to int
ALTER TABLE [SECONDARY] ALTER COLUMN VID_FK INT NOT NULL
ALTER TABLE [HEADER] ALTER COLUMN VID INT NOT NULL
-- re-create constraints
ALTER TABLE [dbo].[HEADER] ADD CONSTRAINT [PK_HEADER] PRIMARY KEY CLUSTERED
(
[vid] ASC
)
ALTER TABLE [dbo].[SECONDARY] WITH CHECK ADD CONSTRAINT [FK_SECONDARY_HEADER]
FOREIGN KEY([vid_fk]) REFERENCES [dbo].[HEADER] ([vid])
-- put data back
INSERT INTO [Header]
SELECT * FROM #HeaderTemp
INSERT INTO [Secondary]
SELECT * FROM #SecondaryTemp
COMMIT TRANSACTION