I have hit a problem with SQL Server that results in it infinitely recompiling a function.
To reproduce, create a new database with the option Parameterization = Forced or execute the following on an existing DB:
ALTER DATABASE [DatabaseName] SET PARAMETERIZATION FORCED WITH NO_WAIT
Then execute the following script:
CREATE TABLE dbo.TestTable(
ID int IDENTITY(1,1) NOT NULL,
FullTextField varchar(100) NULL,
CONSTRAINT PK_TestTable PRIMARY KEY CLUSTERED
(ID ASC)
)
GO
IF NOT EXISTS(SELECT 1 FROM sysfulltextcatalogs WHERE name = 'FullTextCat')
CREATE FULLTEXT CATALOG FullTextCat;
GO
CREATE FULLTEXT INDEX ON dbo.TestTable (FullTextField) KEY INDEX PK_TestTable
ON FullTextCat
WITH
CHANGE_TRACKING AUTO
GO
CREATE OR ALTER FUNCTION dbo.fn_TestFullTextSearch(#Filter VARCHAR(8000))
RETURNS TABLE
AS
RETURN SELECT
ID,
FullTextField
FROM dbo.TestTable
WHERE CONTAINS(FullTextField, #Filter)
GO
SELECT * FROM dbo.fn_TestFullTextSearch('"a*"')
The query will never return. Running SQL Profiler to monitor SP:CacheInsert and SP:CacheRemove will show SQL server is doing this endlessly and the SQL logs will show countless "A possible infinite recompile was detected for SQLHANDLE" messages.
Setting the Parameterization = Simple works around the issue but we need this to be set to Forced for other reasons.
Has anyone come across this issue before and/or have a suggested solution?
Thanks,
Chuck
While I still experience the problem with the original code I provided, by following #Martin's approach of explicitly parameterizing the call to the function:
EXEC sys.sp_executesql N'SELECT * FROM dbo.fn_TestFullTextSearch(#Filter)', N'#Filter VARCHAR(4)', #Filter = '"a*"'
I have been able to successfully work around the problem.
In my installer I have to make a minor change in the schema:
IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'[dbo].[UserProfiles]') AND name = 'AllCheckboxesChecked')
BEGIN
ALTER TABLE [dbo].[UserProfiles] ADD [AllCheckboxesChecked] [bit] CONSTRAINT [DF_UserProfiles_AllCheckboxesChecked] DEFAULT 0 NOT NULL
UPDATE [dbo].[UserProfiles] SET [AllCheckboxesChecked]=1 WHERE [CheckedBoxes] LIKE '%#ALL#%'
END
GO
In SSMS this works, but not in Advanced Installer, where it fails with the error message that the column AllCheckboxesChecked does not exist. So I tried:
IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'[dbo].[UserProfiles]') AND name = 'AllCheckboxesChecked')
BEGIN
ALTER TABLE [dbo].[UserProfiles] ADD [AllCheckboxesChecked] [bit] CONSTRAINT [DF_UserProfiles_AllCheckboxesChecked] DEFAULT 0 NOT NULL
GO
UPDATE [dbo].[UserProfiles] SET [AllCheckboxesChecked]=1 WHERE [CheckedBoxes] LIKE '%#ALL#%'
END
GO
but this throws syntax errors as well (not in SSMS, only in AdvInst), so I guess that GO is not allowed inside the BEGIN...END block. The connection is configured as follows:
Connection type: Microsoft SQL Server / MSDE
Connection mode: ODBC Driver
ODBC Driver: SQL Server
Use 64-bit ODBC resource: No
What steps can I take to get the column created and populated with correct values iff the installer runs on a DB where the column does not yet exist?
The column doesn't exist error is due to validation that occurs on existing objects. Since the table already exists, the parser / compiler will verify that the table contains all of the referenced columns.
In order to get around such timing issues with object verification, you can enclose the statement in an EXEC which will not be verified until run-time:
BEGIN
ALTER TABLE [dbo].[UserProfiles]
ADD [AllCheckboxesChecked] [bit]
CONSTRAINT [DF_UserProfiles_AllCheckboxesChecked] DEFAULT 0
NOT NULL;
EXEC(N'UPDATE [dbo].[UserProfiles]
SET [AllCheckboxesChecked]=1
WHERE [CheckedBoxes] LIKE ''%#ALL#%''');
END;
The GO statement is a batch terminator and items in a batch only get committed at the end of the batch, either at the next GO statement or when the end of the script is reached. In your case the batch containing the ALTER COLUMN statement has not yet been committed and therefore you get an error that the column does not exist. You will have to split up your scripts in two parts.
GO is a batch terminator - it's specific to the tool you are using rather than SQL Server - so if you put
Statement1
GO
Statemetn2
That will be sent to SQL Server as two separate executions (batches).
Basically you are breaking your query up into two batches and the first batch you aren't closing the BEGIN block and in the second batch you aren't startinng the END block!
I am working on SDL Server 2008 R2, where I generated a schema-only database script. The generated script is as follows:
ALTER TABLE [dbo].[ConsoleServer] WITH CHECK ADD CONSTRAINT [FK_ConsoleServer_RackUnits] FOREIGN KEY([RackUnitID])
REFERENCES [dbo].[RackUnits] ([UnitID])
GO
ALTER TABLE [dbo].[ConsoleServer] CHECK CONSTRAINT [FK_ConsoleServer_RackUnits]
I have these 2 questions:-
I know that the first line is responsible to create a FK between two DB tables. but what is the purpose of the following :
ALTER TABLE [dbo].[ConsoleServer] CHECK CONSTRAINT [FK_ConsoleServer_RackUnits]
In general, why does the DB script have the word GO. Now if I remove it the script will be executed well on the destination DB, so why it is included in the script prior to any statement?
The ALTER TABLE ... CHECK CONSTRAINT ... line enables the constraint. You can add a constraint and leave it disable (while you clean up the data for example). See more here
GO is a batch separator, it's only recognized by SSMS. Some statements, such as CREATE PROCEDURE... requires it to be the first statement in the batch. You can type it out in a new file, or use GO to terminate the previous batch. Don't send GO from your application through OLEDB or ADO.NET though.
Alter Collation
I need to change the collation of one of our databases on a particular server from Latin1_General_CI_AS to SQL_Latin1_General_CP1_CI_AI so that it matches the rest of our databases.
The Problem
However, when I attempt to do this, I get the following error:
ALTER DATABASE failed. The default collation of database 'XxxxxXxxxxx' cannot be set to SQL_Latin1_General_CP1_CI_AI. (Microsoft SQL Server, Error: 5075)
My Research
My googling on the topic has revealed a number of articles which indicate that I need to export all the data, drop the database, re-create it with the correct collation, then re-import the data.
For example: Problem with database collation change (SQL Server 2008)
Obviously this is a significant task, especially since primary-foreign key relationships must be preserved, and our database is quite large (over ten million data rows).
My Question
Is there a way to change the collation of an existing SQL Server 2012 database which does not require exporting and re-importing all the data?
Alternatively, is there some tool or script(s) which can automate this process in a reliable manner?
The following works for me on SQL Server 2012:
ALTER DATABASE CURRENT COLLATE SQL_Latin1_General_CP1_CI_AI;
The accepted answer in the linked question is not entirely correct, at least not for SQL Server 2012. It says:
Ahh, this is one of the worst problems in SQL Server: you cannot change the collation once an object is created (this is true both for tables and databases...).
But I was just able to change the default collation and I have tables that are populated. The MSDN page for ALTER DATABASE states in the "Remarks" section, under "Changing the Database Collation":
Before you apply a different collation to a database, make sure that the following conditions are in place:
You are the only one currently using the database.
No schema-bound object depends on the collation of the database.
If the following objects, which depend on the database collation, exist in the database, the ALTER DATABASE database_name COLLATE statement will fail. SQL Server will return an error message for each object blocking the ALTER action:
User-defined functions and views created with SCHEMABINDING.
Computed columns.
CHECK constraints.
Table-valued functions that return tables with character columns with collations inherited from the default database collation.
So, I would suggest making sure that the database is in Single-User mode, and that if you have any of those four items, that you:
drop them
change the collation
and then re-add them
BUT, at that point all that has been changed is the Database's default Collation. The Collation of any existing columns in user tables (i.e. non-system tables) will still have the original Collation. If you want existing string columns -- CHAR, VARCHAR, NCHAR, NVARCHAR, and the deprecated TEXT and NTEXT -- to take on the new Collation, you need to change each of those columns individually. And, if there are any indexes defined on those columns, then those indexes will need to be dropped first (disabling is not enough) and created again after the ALTER COLUMN (other dependencies that would prevent the ALTER COLUMN would have already been dropped in order to get the ALTER DATABASE to work). The example below illustrates this behavior:
Test Setup
USE [tempdb];
SET NOCOUNT ON;
CREATE TABLE dbo.ChangeCollationParent
(
[ChangeCollationParentID] INT NOT NULL IDENTITY(1, 1)
CONSTRAINT [PK_ChangeCollationParent] PRIMARY KEY,
ExtendedASCIIString VARCHAR(50) COLLATE Latin1_General_CI_AS NULL,
UnicodeString NVARCHAR(50) COLLATE Latin1_General_CI_AS NULL
);
CREATE TABLE dbo.ChangeCollationChild
(
[ChangeCollationChildID] INT NOT NULL IDENTITY(1, 1)
CONSTRAINT [PK_ChangeCollationChild] PRIMARY KEY,
[ChangeCollationParentID] INT NULL
CONSTRAINT [FK_ChangeCollationChild_ChangeCollationParent] FOREIGN KEY
REFERENCES dbo.ChangeCollationParent([ChangeCollationParentID]),
ExtendedASCIIString VARCHAR(50) COLLATE Latin1_General_CI_AS NULL,
UnicodeString NVARCHAR(50) COLLATE Latin1_General_CI_AS NULL
);
INSERT INTO dbo.ChangeCollationParent ([ExtendedASCIIString], [UnicodeString])
VALUES ('test1' + CHAR(200), N'test1' + NCHAR(200));
INSERT INTO dbo.ChangeCollationParent ([ExtendedASCIIString], [UnicodeString])
VALUES ('test2' + CHAR(170), N'test2' + NCHAR(170));
INSERT INTO dbo.ChangeCollationChild
([ChangeCollationParentID], [ExtendedASCIIString], [UnicodeString])
VALUES (1, 'testA ' + CHAR(200), N'testA ' + NCHAR(200));
INSERT INTO dbo.ChangeCollationChild
([ChangeCollationParentID], [ExtendedASCIIString], [UnicodeString])
VALUES (1, 'testB ' + CHAR(170), N'testB ' + NCHAR(170));
SELECT * FROM dbo.ChangeCollationParent;
SELECT * FROM dbo.ChangeCollationChild;
Test 1: Change column Collation with no dependencies
ALTER TABLE dbo.ChangeCollationParent
ALTER COLUMN [ExtendedASCIIString] VARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AI NULL;
ALTER TABLE dbo.ChangeCollationParent
ALTER COLUMN [UnicodeString] NVARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AI NULL;
ALTER TABLE dbo.ChangeCollationChild
ALTER COLUMN [ExtendedASCIIString] VARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AI NULL;
ALTER TABLE dbo.ChangeCollationChild
ALTER COLUMN [UnicodeString] NVARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AI NULL;
SELECT * FROM dbo.ChangeCollationParent;
SELECT * FROM dbo.ChangeCollationChild;
The ALTER COLUMN statements above complete successfully.
Test 2: Change column Collation with dependencies
-- First, create an index:
CREATE NONCLUSTERED INDEX [IX_ChangeCollationParent_ExtendedASCIIString]
ON dbo.ChangeCollationParent ([ExtendedASCIIString] ASC);
-- Next, change the Collation back to the original setting:
ALTER TABLE dbo.ChangeCollationParent
ALTER COLUMN [ExtendedASCIIString] VARCHAR(50) COLLATE Latin1_General_CI_AS NULL;
This time, the ALTER COLUMN statement received the following error:
Msg 5074, Level 16, State 1, Line 60
The index 'IX_ChangeCollationParent_ExtendedASCIIString' is dependent on column 'ExtendedASCIIString'.
Msg 4922, Level 16, State 9, Line 60
ALTER TABLE ALTER COLUMN ExtendedASCIIString failed because one or more objects access this column.
ALSO, please be aware that the Collation of some string columns in database-scoped system catalog views (e.g. sys.objects, sys.columns, sys.indexes, etc) will change to the new Collation. If your code has JOINs to any of these string columns (i.e. name), then you might start getting Collation mismatch errors until you change the Collation on the joining columns in your user tables.
UPDATE:
If changing the Collation for the entire Instance is the desire, or an option, then there is an easier method that bypasses all of these restrictions. It is undocumented and hence unsupported (so if it doesn't work, Microsoft isn't going to help). However, it changes the Collation at all levels: Instance, all Database's, and all string columns in all User Tables. It does this, and avoids all of the typical restrictions, by simply updating the meta-data of the tables, etc to have the new Collation. It then drops and recreates all indexes that have string columns. There are also a few nuances to this method that might have impact, but are fixable. This method is the -q command-line switch of sqlservr.exe. I have documented all of the behaviors, including listing all of the potentially affected areas by doing such a wide-sweeping Collation change, in the following post:
Changing the Collation of the Instance, the Databases, and All Columns in All User Databases: What Could Possibly Go Wrong?
For anyone else stumbling to this problem, the solution is to set DB in single_user mode before change the collation and then set again the multi_user mode after it.
Make sure to not close the connection before setting the multi_user mode!
/* block all other users from connecting to the db */
ALTER DATABASE YorDbName SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
/* modify your db collate */
ALTER DATABASE CURRENT COLLATE SQL_Latin1_General_CP1_CI_AI;
/* allow again all other users to connect to the db */
ALTER DATABASE YorDbName SET MULTI_USER;
I've been comparing databases using vs 2010 scheme comparing tool and it generated some stuff which is not clear. For example at the end of the script it has this statement:
ALTER TABLE [dbo].[My_table] WITH CHECK CHECK CONSTRAINT [FK_FOREIGN_ID];
Can anyone explain what this means?
It means that existing data should be checked against the constraint when it is added.
Failure to have the CHECK CHECK leaves your constraints untrusted and they cannot be used by the query optimiser.
That tells SQL Server to validate the constraint against new rows. The counter example would be to use WITH NOCHECK to temporarily disable validation checks for new rows.
ALTER TABLE (Transact-SQL) (WITH CHECK | WITH NOCHECK )