Migrate Team Foundation Server’s SQL Server to another SQL Server (same or new version)

Yesterday we migrated SharePoint’s SQL Server to a new instance today we will be migrating Team Foundation Server (TFS) SQL database to new SQL instance.
In this example we are migrating it to a new version of SQL Server which is from SQL 2008 R2 to SQL 2012, this will also work on the same SQL version.

What we had done here is a very simple step by step instructions and this is what I’ve done with our live environment. I only migrated the Project Collection and Configuration Database so if you have the same scenario you can follow this with care of course. So let’s start.

Moving TFS Project Collections

1. Detach Project Collection

1 Detach Project Collection

Go to Team Foundation Server Administration Console -> Application Tier -> Team Project Collections then choose the collection you want to migrate. Once chosen choose detach then click next on each following step to confirm.

Once finished you will see a Window similar to this, click Complete then close.

1b Detach Project Collection

2. Detach you TFS_Configuration Database in SQL Server

2 Detach SQL Server Instance

3. Copy the Database Related Files

Copy all database related files (.mdf, .ldf) to the New SQL Server

3 Copy Detached Database Files to New SQL Server

4. Attach the Database in the new SQL Server

4 Attach Database

5. Attach Project Collection

Go to Team Foundation Server Administration Console -> Application Tier -> Team Project Collections then choose Attach Collection.

5 Attach Project Collection

5b Attach Project Collection

Once finished you will see a Window similar to this, click Complete then close.

5c Attach Project Collection

At this stage try doing a check in and check out to see if everthing worked, compare file history as well to double check

Moving TFS Configuration Database

1. Stop IIS on TFS Server

Using the Admin context execute “iisreset /stop” at the command prompt.

If you can’t stop IIS there is another alternative. Just stop the TFS related sites and application pool in the Server

Stop TFS Website on Server

1 Stop TFS Website

Stop Application Pools, those two highlighted in the screenshot below

2 Stop TFS Application Pools

2. Stop TFS Job Agent service

You can stop this via services.msc or execute “net stop TfsJobAgent” at the command prompt.

3 Stop TFS Services

3. Detach the Tfs_Configuration database

Do it similarly with the step 2 on migrating the project collection

4. Attach the Tfs_Configuration to the new SQL Server instance

5. Prepare new SQL for TFS instance

Using Admin context open again Command Prompt if you had closed it then navigate to “C:\Program Files\Microsoft Team Foundation Server 2010\Tools”
Execute “TfsConfig prepSql /sqlInstance:{your_new_server_name_here}”

3 Prepare SQL Server for TFS

6. Register new SQL for TFS instance

In the same command prompt instance Execute “TfsConfig registerDb /sqlInstance:{your_new_server_name_here} /databaseName:Tfs_Configuration”

4 Register SQL Server instance for TFS

7. Reapply Service Account

Go to Team Foundation Server Administration Console -> Application Tier then click on Reapply Account.
Select the account you use then click OK

5 Reapply Account

 

If successful you will see something like this

5 Reapply Account Message

8. Start the TFS Job Agent service

Start the TFS Job Agent service via services.msc or execute “net start TfsJobAgent” at the command prompt.

9. Start IIS on TFS Server

Using the Admin context execute “iisreset /start” at the command prompt.
If earlier you just stopped the TFS related sites and application pool just run it all back again.

You have now successfully migrated your database.

Migrate SharePoint’s SQL Server to another SQL Server (same or new version)

This is a step by step guide on how you migrate your SharePoint’s (I used 2010 version in this instance) SQL Server to an new SQL Server host.  In this exercise I tried migrating it in a new version of SQL server which is 2012 from 2008 R2, also had tried it in the same version.  There are only 9 steps to perform this task but it takes into consideration you already have a new SQL Server provisioned.

Lets start

1. Stop all SharePoint and IIS Related Services

  • SharePoint 2010 Administration
  • SharePoint 2010 Timer
  • SharePoint 2010 Tracing
  • SharePoint 2010 User Code Host
  • SharePoint 2010 VSS Writer
  • SharePoint Foundation Search V4
  • World Wide Web Publishing Service
  • SharePoint Server Search 14
  • Web Analytics Data Processing Service
  • Web Analytics Web Service

1 Stop Services

2. Stop IIS
At the command prompt under admin context, type iisreset /stop

3. Detach all related SQL Server databases

  • Configuration database
  • Central Administration content database
  • Content databases
  • Service application databases

i.e.
Bdc_Service_DB_xxxxxxxxxxxxxx
SharePoint_AdminContent_xxxxxxxxxxxxx
SharePoint_Config
WSS_Content
WSS_Logging
WSS_Search_xxxxxxxxxxxxxx

2 Detach Database

4. Move all database files (.mdf, .ldf, and .ndf) to the new server.

Place it in the data folder of the SQL Server instance of the new host.

3-copy-database-files

5. Set up same user permissions on the new SQL server

best way to do this is when you use AD accounts is by scripting the users on the old server and running that script on the new server. If you are not using AD accounts then you will have issues with passwords so best way to do that is to follow this -> http://support.microsoft.com/kb/918992

6. Attach your databases to the new SQL Server

4 Attach Database

5 Attach Database 2

7. Verify what ports your New SQL is using

If it’s using a dynamic port change it to static. To do this go to SQL Server Configuration Manager and into the SQL Server Network Configuration Section choose the instance you will be using. Go to the TCP/IP section and if TCP Dynamic Ports are set to 0 (zero) then remove that and assign a static port into the TCP Port Section on all IP’s. For this sample we will be using 14330 <- notice I just added 0 at the end from the default port. Note that as we will need that on the next section also if you changed the port number from dynamic to static you will need to restart your SQL Server before it can take effect.

6 Port Allocation

8. Go to your SharePoint server and create your SQL Server Alias

If SQL Configuration Manager is installed just head over to the SQL Native Client 10.0 configuration then into Aliases. Create a new alias that points to your new SQL Server by right clicking on the Alias. You need to do this on both 32 and 64 bit instances.

7 Alias Configuration

  • On the Alias name put your old SQL Server IP or Name
  • On the Port the port you indicated on step 7, in this case 14330
  • Protocol should be TCP/IP
  • And on Server the new SQL Server IP or Name together with its instance name if there is any. In our case it’s SQL2012

7 Alias Configuration 2

If you don’t have SQL Configuration Manager in your SharePoint Server then run the CLICONFIG again on both 32 and 64 bit instances. For 32 bit go to run command and execute this “%SystemRoot%/SysWow64/CliConfg.exe” and for 64 just this “CLICONFG” all without quotes of course. Add the same info as above.

7 Alias Configuration b

9. Start all your SharePoint services, now you have successfully migrated your database.  

  • SharePoint 2010 Administration
  • SharePoint 2010 Timer
  • SharePoint 2010 Tracing
  • SharePoint 2010 User Code Host
  • SharePoint 2010 VSS Writer
  • SharePoint Foundation Search V4
  • World Wide Web Publishing Service
  • SharePoint Server Search 14
  • Web Analytics Data Processing Service
  • Web Analytics Web Service

At the command prompt under admin context, type iisreset /start.

How to Synchronize logins/users between Mirrored Servers

Now that you have mirrored your servers, it does not mean that your users are also mirrored (isn’t it nice to have this feature included in next SQL Server releases).  You should configure your users properly as well so when a disaster hits you wont have a login error issue when your application user tries to connect to the failover partner and that server cannot recognize that user.

Now you might be wondering why you did added the same user name in both mirrored servers but got a login failed exception. While you think you created exactly the same login they are not 100% the same as they have different SID’s (Security Identifier). To verify that run this TSQL in both servers that are mirrored where you had created your user.

SELECT SID, Name from syslogins

As you can see we created a user called “SampleLogin” in both serves but different SID’s were given, this is what gives you a similar error like this.

Cannot open database “Database Name” requested by the login. The login failed. Login failed for user ‘SampleLogin’.

Here is a screenshot.

Now to prevent that from happening the user should have the same SID and you have to indicate that on creation of the user. So here are the steps, first create your login by issuing this command on your first server, take note that the user is enclosed with brackets

CREATE LOGIN [YourUser] WITH PASSWORD = 'YourComplicatedPassword'

Once you have successfully created the user run this command to get the SID

SELECT SID from syslogins WHERE Name = 'YourUser'

Now into your second server, create a user with the same info but with the SID from the query result you got from the first server (the query above)

CREATE LOGIN [YourUser] WITH PASSWORD = 'YourComplicatedPassword', SID = {your SID here}

Now you have the exact user and all you need to do is assign the proper roles and rights on both servers, assign only on the active one as you can’t assign on the one mirroring, once done on the active, failover your database and assign same roles and rights on the other server.  Then you are all good to go.

UPDATE : You still have to run

EXEC sp_change_users_login 'Auto_Fix', 'YourUser'

To fix the user

Altering Mirroring Endpoints (Ports) on SQL Server

We are in a middle of a SQL Server 2012 migration project and one of the main tasks is to completely mirror all of the databases. Having said that it does not mean we did not mirrored it before, some of the legacy databases are already mirroring and we have one dedicated server to do the job and that’s what it only does. With this project we want to install a new instance of SQL Server 2012 to be as witness for other SQL Server 2012 databases on top of SQL Server 2008 already installed in the server, so after installation we then setup the mirroring and then we had this error:

TITLE: Database Properties
------------------------------

An error occurred while starting mirroring.

------------------------------
ADDITIONAL INFORMATION:

Alter failed for Database 'YourDatabase'.  (Microsoft.SqlServer.Smo)

For help, click: http://go.microsoft.com/fwlink?ProdName=Microsoft+SQL+Server&ProdVer=11.0.2100.60+((SQL11_RTM).120210-1917+)&EvtSrc=Microsoft.SqlServer.Management.Smo.ExceptionTemplates.FailedOperationExceptionText&EvtID=Alter+Database&LinkId=20476

------------------------------

An exception occurred while executing a Transact-SQL statement or batch. (Microsoft.SqlServer.ConnectionInfo)

------------------------------

The server network address TCP://servername:5022 can not be reached or does not exist. Check the network address name and that the ports for the local and remote endpoints are operational. (Microsoft SQL Server, Error: 1418)

For help, click: http://go.microsoft.com/fwlink?ProdName=Microsoft%20SQL%20Server&ProdVer=11.00.2100&EvtSrc=MSSQLServer&EvtID=1418&LinkId=20476

------------------------------
BUTTONS:

OK
------------------------------

Now that is giving you an exception because port 5022 is used by the old SQL Server Witness, which means you need to change this port so that it would work.

Run this TSQL command in your Witness server instance to check if indeed 5022 is being used and at the same time what other ports you can use

SELECT * FROM sys.tcp_endpoints

To change that all you have to do is select a port number not on the list below as well as any port number that is not used by your server and applications, so for this example lets use 5023, you have to issue this command on your SQL Server Witness

ALTER ENDPOINT [Mirroring] AS TCP (listener_port = 5023)

Once done restart you will see “command(s) completed successfully“, you need to restart the SQL Server for it to take effect.  Now go back and configure your mirror again and it will now connect to the 5023 port

The Easy way of changing Collation of all Database objects in SQL Server

Did you encountered a problem where you wanted to change your database collation to default or even just changing it to a different type? I guess what you had initially done (like me) was to change the collation of the Database

well that does not quite work well as the existing columns will not be changed and retain its current collation type, only the newly created objects will use this new collation type. So you are left to the option of changing the columns one at a time by going to the column property and restoring it to default or choosing the collation type you want.

Well that’s great if you need to change 10 columns or less but what if you want to change the whole database? What if it’s a primary key or a foreign key? Well isn’t that a nightmare? Well I will give you an easy solution and all you need to do is to run 6 easy steps. If you don’t want to recreate the database and pump data by using DTS or SSIS then this is the solution for you just make sure backup and restore everything before doing any changes.

Step 1: Prepare your DB and Change the collation to your desired one.

Like I had said backup your database as a part of the preparation, once that’s done change your collation to the desired type by going to the database properties by right clicking on the database and choosing properties, once your on the properties window choose options and you can see the collation from there, choose what you want then hit OK. This will ensure that new objects created will be using the new collation.

Step 2: Create you Change Collation Script.

Next is to create a script to change the collation of every object in your database. You need to use the information_schema to extract columns needed to be changed and from there we run a loop on all objects creating alter scripts on each item. Since it is a collation change we will only need fields that uses character types and text types. What you need is to have a lot of commands similar to this

ALTER TABLE TABLENAME ALTER COLUMN COLUMNNAME varchar(100) COLLATE Latin1_General_CI_AS NULL

So here is the code to generate that

OPEN MyTableCursor

FETCH NEXT FROM MyTableCursor INTO @TableName
WHILE @@FETCH_STATUS = 0
	BEGIN
		DECLARE MyColumnCursor Cursor
		FOR 
		SELECT COLUMN_NAME,DATA_TYPE, CHARACTER_MAXIMUM_LENGTH,IS_NULLABLE from information_schema.columns
			WHERE table_name = @TableName AND  (Data_Type LIKE '%char%' OR Data_Type LIKE '%text%') AND COLLATION_NAME <> @CollationName
			ORDER BY ordinal_position 
		Open MyColumnCursor

		FETCH NEXT FROM MyColumnCursor INTO @ColumnName, @DataType, @CharacterMaxLen, @IsNullable
		WHILE @@FETCH_STATUS = 0
			BEGIN
			SET @SQLText = 'ALTER TABLE ' + @TableName + ' ALTER COLUMN [' + @ColumnName + '] ' + @DataType + '(' + CASE WHEN @CharacterMaxLen = -THEN 'MAX' ELSE @CharacterMaxLen END + ') COLLATE ' + @CollationName + ' ' + CASE WHEN @IsNullable = 'NO' THEN 'NOT NULL' ELSE 'NULL' END
			PRINT @SQLText 

		FETCH NEXT FROM MyColumnCursor INTO @ColumnName, @DataType, @CharacterMaxLen, @IsNullable
		END
		CLOSE MyColumnCursor
		DEALLOCATE MyColumnCursor

FETCH NEXT FROM MyTableCursor INTO @TableName
END
CLOSE MyTableCursor
DEALLOCATE MyTableCursor

Run it then save the script for later use. Lets call the script “ChangeCollation.sql”

If you don’t have relationships, primary keys and foreign keys then you don’t need to do the next step.

Step 3: Create a Stored Procedure to Script Indexes and Relationships

Well if you have relationships, primary keys and foreign keys then that’s a good practice but you need to script them as you need to drop those before changing the collation. Initially I thought I can do this with the wizard and choose to script indexes but it does not create on its own the table creation is always included so with a little help from Google I don’t have to write a single piece of code. I found this really good script to do it and I got it from here http://sqlblog.com/blogs/adam_machanic/archive/2010/04/04/rejuvinated-script-creates-and-drops-for-candidate-keys-and-referencing-foreign-keys.aspx
I only separated the Create Indexes and Drop Indexes as we need to run a process in the middle.

Here is the Create Index script courtesy of Adam Machanic

/*
Script Table Keys
(C) 2010 Adam Machanic - amachanic@gmail.com
http://sqlblog.com/blogs/adam_machanic/archive/2010/04/04/rejuvinated-script-creates-and-drops-for-candidate-keys-and-referencing-foreign-keys.aspx
This script produces a script of all of the candidate keys (primary keys or unique 
constraints) as well as referencing foreign keys, for the target table. To use, put
SSMS into "results in text" mode and run the script. The output will be a formatted
script that you can cut and paste to use elsewhere.

Don't forget to configure the maximum text size before using. The default is 256
characters--not enough for many cases.

Tools->Options->Query Results->Results to Text->Maximum number of characters->8192
*/
CREATE PROC [dbo].[ScriptCreateTableKeys]
	@table_name SYSNAME
AS
BEGIN
	SET NOCOUNT ON

	--Note: Disabled keys and constraints are ignored
	--TODO: Drop and re-create referencing XML indexes, FTS catalogs

	DECLARE @crlf CHAR(2)
	SET @crlf = CHAR(13) + CHAR(10)
	DECLARE @version CHAR(4)
	SET @version = SUBSTRING(@@VERSION, LEN('Microsoft SQL Server') + 2, 4)
	DECLARE @object_id INT
	SET @object_id = OBJECT_ID(@table_name)
	DECLARE @sql NVARCHAR(MAX)

	IF @version NOT IN ('2005', '2008')
	BEGIN
		RAISERROR('This script only supports SQL Server 2005 and 2008', 16, 1)
		RETURN
	END

	SET @sql = '' +
		'SELECT ' +
			'CASE ' +
				'WHEN 1 IN (i.is_primary_key, i.is_unique_constraint) THEN ' +
					'''ALTER TABLE '' + ' +
						'QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id)) + ''.'' + ' +
						'QUOTENAME(OBJECT_NAME(i.object_id)) + @crlf + ' +
					'''ADD '' + ' +
						'CASE k.is_system_named ' +
							'WHEN 0 THEN ''CONSTRAINT '' + QUOTENAME(k.name) + @crlf ' +
							'ELSE '''' ' +
						'END + ' +
					'CASE k.type ' +
						'WHEN ''UQ'' THEN ''UNIQUE'' ' +
						'ELSE ''PRIMARY KEY'' ' +
					'END + '' '' + ' +
					'i.type_desc  + @crlf + ' +
					'kc.key_columns + @crlf ' +
				'ELSE ' +
					'''CREATE UNIQUE '' + i.type_desc + '' INDEX '' + ' +
						'QUOTENAME(i.name) + @crlf + ' +
					'''ON '' + ' +
						'QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id)) + ''.'' + ' +
						'QUOTENAME(OBJECT_NAME(i.object_id)) + @crlf + ' +
					'kc.key_columns + @crlf + ' +
					'COALESCE ' +
					'( ' +
						'''INCLUDE '' + @crlf + ' +
						'''( '' + @crlf + ' +
							'STUFF ' +
							'( ' +
								'( ' +
									'SELECT ' +
									'( ' +
										'SELECT ' +
											''','' + @crlf + '' '' + QUOTENAME(c.name) AS [text()] ' +
										'FROM sys.index_columns AS ic ' +
										'JOIN sys.columns AS c ON ' +
											'c.object_id = ic.object_id ' +
											'AND c.column_id = ic.column_id ' +
										'WHERE ' +
											'ic.object_id = i.object_id ' +
											'AND ic.index_id = i.index_id ' +
											'AND ic.is_included_column = 1 ' +
										'ORDER BY ' +
											'ic.key_ordinal ' +
										'FOR XML PATH(''''), TYPE ' +
									').value(''.'', ''VARCHAR(MAX)'') ' +
								'), ' +
								'1, ' +
								'3, ' +
								''''' ' +
							') + @crlf + ' +
						''')'' + @crlf, ' +
						''''' ' +
					') ' +
			'END + ' +
			'''WITH '' + @crlf + ' +
			'''('' + @crlf + ' +
				''' PAD_INDEX = '' + ' +
						'CASE CONVERT(VARCHAR, i.is_padded) ' +
							'WHEN 1 THEN ''ON'' ' +
							'ELSE ''OFF'' ' +
						'END + '','' + @crlf + ' +
				'CASE i.fill_factor ' +
					'WHEN 0 THEN '''' ' +
					'ELSE ' +
						''' FILLFACTOR = '' + ' +
								'CONVERT(VARCHAR, i.fill_factor) + '','' + @crlf ' +
				'END + ' +
				''' IGNORE_DUP_KEY = '' + ' +
						'CASE CONVERT(VARCHAR, i.ignore_dup_key) ' +
							'WHEN 1 THEN ''ON'' ' +
							'ELSE ''OFF'' ' +
						'END + '','' + @crlf + ' +
				''' ALLOW_ROW_LOCKS = '' + ' +
						'CASE CONVERT(VARCHAR, i.allow_row_locks) ' +
							'WHEN 1 THEN ''ON'' ' +
							'ELSE ''OFF'' ' +
						'END + '','' + @crlf + ' +
				''' ALLOW_PAGE_LOCKS = '' + ' +
						'CASE CONVERT(VARCHAR, i.allow_page_locks) ' +
							'WHEN 1 THEN ''ON'' ' +
							'ELSE ''OFF'' ' +
						'END + ' +
				CASE @version
					WHEN '2005' THEN ''
					ELSE			 
						''','' + @crlf + ' +
						''' DATA_COMPRESSION = '' + ' +
							'( ' +
								'SELECT ' +
									'CASE ' +
										'WHEN MIN(p.data_compression_desc) = MAX(p.data_compression_desc) THEN MAX(p.data_compression_desc) ' +
										'ELSE ''[PARTITIONS USE MULTIPLE COMPRESSION TYPES]'' ' +
									'END ' +
								'FROM sys.partitions AS p ' +
								'WHERE ' +
									'p.object_id = i.object_id ' +
									'AND p.index_id = i.index_id ' +
							') '
				END + '+ @crlf + ' +
			''') '' + @crlf + ' +
			'''ON '' + ds.data_space + '';'' + ' +
				'@crlf + @crlf COLLATE database_default AS [-- Create Candidate Keys] ' +
		'FROM sys.indexes AS i ' +
		'LEFT OUTER JOIN sys.key_constraints AS k ON ' +
			'k.parent_object_id = i.object_id ' +
			'AND k.unique_index_id = i.index_id ' +
		'CROSS APPLY ' +
		'( ' +
			'SELECT ' +
				'''( '' + @crlf + ' +
					'STUFF ' +
					'( ' +
						'( ' +
							'SELECT ' +
							'( ' +
								'SELECT ' +
									''','' + @crlf + '' '' + QUOTENAME(c.name) AS [text()] ' +
								'FROM sys.index_columns AS ic ' +
								'JOIN sys.columns AS c ON ' +
									'c.object_id = ic.object_id ' +
									'AND c.column_id = ic.column_id ' +
								'WHERE ' +
									'ic.object_id = i.object_id ' +
									'AND ic.index_id = i.index_id ' +
									'AND ic.key_ordinal > 0 ' +
								'ORDER BY ' +
									'ic.key_ordinal ' +
								'FOR XML PATH(''''), TYPE ' +
							').value(''.'', ''VARCHAR(MAX)'') ' +
						'), ' +
						'1, ' +
						'3, ' +
						''''' ' +
					') + @crlf + ' +
				''')'' ' +
		') AS kc (key_columns) ' +
		'CROSS APPLY ' +
		'( ' +
			'SELECT ' +
				'QUOTENAME(d.name) + ' +
					'CASE d.type ' +
						'WHEN ''PS'' THEN ' +
							'+ ' +
							'''('' + ' +
								'( ' +
									'SELECT ' +
										'QUOTENAME(c.name) ' +
									'FROM sys.index_columns AS ic ' +
									'JOIN sys.columns AS c ON ' +
										'c.object_id = ic.object_id ' +
										'AND c.column_id = ic.column_id ' +
									'WHERE ' +
										'ic.object_id = i.object_id ' +
										'AND ic.index_id = i.index_id ' +
										'AND ic.partition_ordinal = 1 ' +
								') + ' +
							''')'' ' +
						'ELSE '''' ' +
					'END ' +
			'FROM sys.data_spaces AS d ' +
			'WHERE ' +
				'd.data_space_id = i.data_space_id ' +
		') AS ds (data_space) ' +
		'WHERE ' +
			'i.object_id = @object_id ' +
			'AND i.is_unique = 1 ' +
			--filtered and hypothetical indexes cannot be candidate keys
			CASE @version
				WHEN '2008' THEN 'AND i.has_filter = 0 '
				ELSE ''
			END +
			'AND i.is_hypothetical = 0 ' +
			'AND i.is_disabled = 0 ' +
		'ORDER BY ' +
			'i.index_id '

	EXEC sp_executesql
@sql,
		N'@object_id INT, @crlf CHAR(2)',
		@object_id, @crlf

	SELECT
		'ALTER TABLE ' + 
			QUOTENAME(OBJECT_SCHEMA_NAME(fk.parent_object_id)) + '.' + 
			QUOTENAME(OBJECT_NAME(fk.parent_object_id)) + @crlf +
		CASE fk.is_not_trusted
			WHEN 0 THEN 'WITH CHECK '
			ELSE 'WITH NOCHECK '
		END + 
			'ADD ' +
				CASE fk.is_system_named
					WHEN 0 THEN 'CONSTRAINT ' + QUOTENAME(name) + @crlf
					ELSE ''
				END +
		'FOREIGN KEY ' + @crlf + 
		'( ' + @crlf + 
			STUFF
(
(
					SELECT
(
						SELECT 
							',' + @crlf + ' ' + QUOTENAME(c.name) AS [text()]
						FROM sys.foreign_key_columns AS fc
						JOIN sys.columns AS c ON
							c.object_id = fc.parent_object_id
							AND c.column_id = fc.parent_column_id
						WHERE 
							fc.constraint_object_id = fk.object_id
						ORDER BY
							fc.constraint_column_id
						FOR XML PATH(''), TYPE
					).value('.', 'VARCHAR(MAX)')
				),
				1,
				3,
				''
			) + @crlf + 
		') ' +
		'REFERENCES ' + 
			QUOTENAME(OBJECT_SCHEMA_NAME(fk.referenced_object_id)) + '.' + 
			QUOTENAME(OBJECT_NAME(fk.referenced_object_id)) + @crlf +
		'( ' + @crlf + 
			STUFF
(
(
					SELECT
(
						SELECT 
							',' + @crlf + ' ' + QUOTENAME(c.name) AS [text()]
						FROM sys.foreign_key_columns AS fc
						JOIN sys.columns AS c ON
							c.object_id = fc.referenced_object_id
							AND c.column_id = fc.referenced_column_id
						WHERE 
							fc.constraint_object_id = fk.object_id
						ORDER BY
							fc.constraint_column_id
						FOR XML PATH(''), TYPE
					).value('.', 'VARCHAR(MAX)')
				),
				1,
				3,
				''
			) + @crlf + 
		');' + 
			@crlf + @crlf COLLATE database_default AS [-- Create Referencing FKs]
	FROM sys.foreign_keys AS fk
	WHERE
		referenced_object_id = @object_id
		AND is_disabled = 0
	ORDER BY
		key_index_id

END

Step 4: Create Stored Procedure to Script Drop Indexes and Relationships

Now you also need to create the drop scripts, these is the other half of Adam Machanic’s script

CREATE PROC [dbo].[ScriptDropTableKeys]
	@table_name SYSNAME
AS
BEGIN
	SET NOCOUNT ON

	--Note: Disabled keys and constraints are ignored
	--TODO: Drop and re-create referencing XML indexes, FTS catalogs

	DECLARE @crlf CHAR(2)
	SET @crlf = CHAR(13) + CHAR(10)
	DECLARE @version CHAR(4)
	SET @version = SUBSTRING(@@VERSION, LEN('Microsoft SQL Server') + 2, 4)
	DECLARE @object_id INT
	SET @object_id = OBJECT_ID(@table_name)
	DECLARE @sql NVARCHAR(MAX)

	IF @version NOT IN ('2005', '2008')
	BEGIN
		RAISERROR('This script only supports SQL Server 2005 and 2008', 16, 1)
		RETURN
	END

	SELECT
		'ALTER TABLE ' + 
			QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id)) + '.' + 
			QUOTENAME(OBJECT_NAME(parent_object_id)) + @crlf +
		'DROP CONSTRAINT ' + QUOTENAME(name) + ';' + 
			@crlf + @crlf COLLATE database_default AS [-- Drop Referencing FKs]
	FROM sys.foreign_keys
	WHERE
		referenced_object_id = @object_id
		AND is_disabled = 0
	ORDER BY
		key_index_id DESC

	SET @sql = '' +
		'SELECT ' +
			'statement AS [-- Drop Candidate Keys] ' +
		'FROM ' +
		'( ' +
			'SELECT ' +
				'CASE ' +
					'WHEN 1 IN (i.is_unique_constraint, i.is_primary_key) THEN ' +
						'''ALTER TABLE '' + ' +
							'QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id)) + ''.'' + ' +
							'QUOTENAME(OBJECT_NAME(i.object_id)) + @crlf + ' +
						'''DROP CONSTRAINT '' + QUOTENAME(i.name) + '';'' + ' +
							'@crlf + @crlf COLLATE database_default ' +
					'ELSE ' +
						'''DROP INDEX '' + QUOTENAME(i.name) + @crlf + ' +
						'''ON '' + ' +
							'QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + ''.'' + ' +
							'QUOTENAME(OBJECT_NAME(object_id)) + '';'' + ' +
								'@crlf + @crlf COLLATE database_default ' +
				'END AS statement, ' +
				'i.index_id ' +
			'FROM sys.indexes AS i ' +
			'WHERE ' +
				'i.object_id = @object_id ' +
				'AND i.is_unique = 1 ' +
				--filtered and hypothetical indexes cannot be candidate keys
				CASE @version
					WHEN '2008' THEN 'AND i.has_filter = 0 '
					ELSE ''
				END +
				'AND i.is_hypothetical = 0 ' +
				'AND i.is_disabled = 0 ' +
		') AS x ' +
		'ORDER BY ' +
			'index_id DESC '

	EXEC sp_executesql 
@sql,
		N'@object_id INT, @crlf CHAR(2)',
		@object_id, @crlf

END

Step 5: Bringing them all together

Now you have the two stored procedure all you have to do is to loop though all tables in you database and pass that as the parameter of the Stored Procedure. First we use the ScriptCreateTableKeys.

DECLARE @TableName nvarchar(255)
DECLARE MyTableCursor Cursor
FOR 
SELECT name FROM sys.tables WHERE [type] = 'U' and name <> 'sysdiagrams' ORDER BY name 
OPEN MyTableCursor

FETCH NEXT FROM MyTableCursor INTO @TableName
WHILE @@FETCH_STATUS = 0
	BEGIN
	EXEC ScriptCreateTableKeys @TableName

	FETCH NEXT FROM MyTableCursor INTO @TableName
END
CLOSE MyTableCursor
DEALLOCATE MyTableCursor

Then lets use the ScriptDropTableKeys

DECLARE @TableName nvarchar(255)
DECLARE MyTableCursor Cursor
FOR 
SELECT name FROM sys.tables WHERE [type] = 'U' and name <> 'sysdiagrams' ORDER BY name 
OPEN MyTableCursor

FETCH NEXT FROM MyTableCursor INTO @TableName
WHILE @@FETCH_STATUS = 0
	BEGIN
	 EXEC ScriptDropTableKeys @TableName

	FETCH NEXT FROM MyTableCursor INTO @TableName
END
CLOSE MyTableCursor
DEALLOCATE MyTableCursor

Just make sure when you execute them output the results as text so you can easily copy and paste the results.
Save the first results as “CreateKeysAndIndexes.sql” and the second as “DropKeysAndIndexes.sql”

Step 6: Run you saved scripts

In this order run your scripts and wait for the results, time wait might vary depending on your database size.

a. DropKeysAndIndexes.sql
b. ChangeCollation.sql
c. CreateKeysAndIndexes.sql

Follow

Get every new post delivered to your Inbox.

Join 774 other followers