SQL Server and Persisted computed field with Hashbytes - sql

Before changes I had a persisted computed field which used Checksum function and index to use it.
alter table Softs add TitleHash AS (CHECKSUM([Title])) PERSISTED;
All were fine until we found that Checksum produces poor hash and duplicates might occur. So we decided to use Hashbytes.
I tried both with binary result and char result
alter table Softs add TitleHashCBin AS (CONVERT(BINARY(16),hashbytes('MD4',[Title]))) PERSISTED;
or
alter table Softs add TitleHashCChar AS (CONVERT(CHAR(32),hashbytes('MD4',[Title]),2)) PERSISTED;
Unfortunately we found that a simple SELECT request does not use index for new field.
SELECT id FROM Softs WHERE TitleHashCBin = 0xC29939F6149FD65100A66AF5FD958D8B
It scans primary index which is build on Id column.
After that we created binary column, copied data from TitleHashCBin and also created index for new column.
alter table Softs add TitleHashBin AS Binary(16)
And used similar select statement.
SELECT id FROM Softs WHERE TitleHashBin = 0xC29939F6149FD65100A66AF5FD958D8B
And this one uses index by TitleHashBin field.
What a hell is going with calculated fields. Can somebody explain what I'm doing wrong or is it a bug?
P.S. Sql Server 2008 10.0.3798
Edit
I just removed char column from the table to investigate what does SSMS generate. It generated actually the same as you described.
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
--DROP INDEXes here
--GO
ALTER TABLE dbo.Softs DROP COLUMN TitleHashCChar, TitleHashChar
GO
ALTER TABLE dbo.Softs SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
So I think we can assume that table options are correct.
After that I repeated select statements but with the same execution plan as before...
Edit
I resolved to task using simple binary fields and Insert/Update triggers to update them. Works like a charm. But it is still unclear why it has so strange behavior?..

Make sure your set options are correct, from BOL Link
SET Option Requirements
The ANSI_NULLS connection-level option must be set to ON when the CREATE TABLE or ALTER TABLE statement that defines the computed column is executed. The OBJECTPROPERTY function reports whether the option is on through the IsAnsiNullsOn property.
The connection on which the index is created, and all connections trying INSERT, UPDATE, or DELETE statements that will change values in the index, must have six SET options set to ON and one option set to OFF. The optimizer ignores an index on a computed column for any SELECT statement executed by a connection that does not have these same option settings.
The NUMERIC_ROUNDABORT option must be set to OFF, and the following options must be set to ON:
ANSI_NULLS
ANSI_PADDING
ANSI_WARNINGS
ARITHABORT
CONCAT_NULL_YIELDS_NULL
QUOTED_IDENTIFIER
Setting ANSI_WARNINGS to ON implicitly sets ARITHABORT to ON when the database compatibility level is set to 90. If the database compatibility level is set to 80 or earlier, the ARITHABORT option must explicitly be set to ON. For more information, see SET Options That Affect Results.

Related

How to modify index type in SQL Server? [duplicate]

This question already has answers here:
Can not drop UNIQUE index from table
(2 answers)
Closed 2 years ago.
I need to change the type of index from "Unique Key" to "index".
I`ve spent 2 days trying to drop and recreate it. Unfortunately, there are a lot of dependencies and the only way to fix my issue is to modify the existing index. SSMS visual editor allows me to change type however I ought to find out how to make it programmatically.
It is really bad idea to use something like UPDATE sys.key_constraints SET type = 'UQ' WHERE ...
Such a solution is not working properly.
I would appreciate any help!
To implement such change you will need to drop and recreate the index.
There is no alter statement that would change it.
It is possible to drop the index as a constant:
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.table_name
DROP CONSTRAINT IX_CONSTAINT_NAME
GO

Setting ANSI Padding OFF with ALTER

I need to turn ANSI PADDING from ON to OFF on an existing field of a table without creating a new table. Is there a way to do this through an ALTER command?
Thanks!
No, there is no way to do this with just ALTER. Like the identity property, collation and some other bits, whether a column applies ANSI padding or not is not something that can be altered after the fact.
You can still do it without creating a new table altogether, but not without creating a new column:
BEGIN TRANSACTION;
SET ANSI_PADDING OFF;
ALTER TABLE T ADD C_badpad CHAR(16) NULL;
EXEC('UPDATE T SET C_badpad = C;')
EXEC sp_rename 'T.C', 'C_old';
EXEC sp_rename 'T.C_badpad', 'C';
EXEC('ALTER TABLE T DROP COLUMN C_old;')
ROLLBACK; -- COMMIT;
But, if you value your sanity, don't do this. Rewrite the client code that wants this so it does its own proper trimming, because the very ability to have ANSI_PADDING set to OFF is deprecated, with good reason. Almost everything expects ANSI_PADDING to be ON these days, so having it OFF for a specific column is likely to break clients that aren't expecting it. Even if you think you're solving a problem now, you're likely going to end up with bigger problems in the future.
ANSI_PADDING is only there as a compatibility tweak for very old software that just doesn't know any better (and will likely want the whole database to be created with ANSI_PADDING = OFF). Don't use it in new development, and certainly don't use it for just one column.

Unable to Add Filtered Index but don't Understand Why

I am trying to update a database from a database project in Visual Studio.
The update script is generated automatically. On line 133, the script adds a filtered index.
CREATE UNIQUE NONCLUSTERED INDEX [ProgramCodes_Value]
ON [dbo].[ProgramCodes]([Value] ASC) WHERE ([IsDeleted]=(0));
But this line produces some errors:
(133,1): SQL72014: .Net SqlClient Data Provider: Msg 1935, Level 16, State 1, Line 1 Cannot create index. Object 'ProgramCodes' was created with the following SET options off: 'ANSI_NULLS'.
(133,0): SQL72045: Script execution error. The executed script:
CREATE UNIQUE NONCLUSTERED INDEX [ProgramCodes_Value]
ON [dbo].[ProgramCodes]([Value] ASC) WHERE ([IsDeleted] = (0));
An error occurred while the batch was being executed.
First off, I really don't understand the ANSI_NULLS thing. Usually, this is just a harmless warning.
Second, I can see it failed when trying to add the filtered index, but I really can't see why. Checking the data, there are no duplicates that would violate this unique index.
Can anyone point me in the right direction to understanding the problem?
From the docs:
SET ANSI_NULLS must also be ON when you are creating or changing indexes on computed columns or indexed views. If SET ANSI_NULLS is OFF, any CREATE, UPDATE, INSERT, and DELETE statements on tables with indexes on computed columns or indexed views will fail. SQL Server will return an error that lists all SET options that violate the required values. Also, when you execute a SELECT statement, if SET ANSI_NULLS is OFF, SQL Server will ignore the index values on computed columns or views and resolve the select operation as if there were no such indexes on the tables or views.

Change IDENTITY property via Primary Key properties?

I have a table structured as such:
PrimaryKey int set as PK
SomeForeignKey int set as FK... linked to some other table's PK
SomeVarChar varchar
SomeDate datetime
When I right-click on Properties of my PK, I noticed that Identity is set to False, Identity Seed is set to 0 and Identity Icrement is set to 0.
However, I am unable to modify these properties. How can I change these values?
Here is a screen shot.
You change the table, not the constraint. Management Studio will take care of the other stuff for you (note that it has to drop the table and re-create it, so if the table is large, be prepared to grab a coffee and hurry up and wait).
Right-click the table in Object Explorer and choose Design
Right-click the column you want to be the identity and choose Properties
In the properties pane, under "Identity Column", choose the correct column:
Click the Save icon (or Ctrl+S):
Note that you may also have to un-check the option Tools > Designers > Table and Database Designers > Prevent saving changes that require table re-creation.
Normally I would suggest making changes to schema using DDL instead of the hokey and bug-ridden GUI, but this is one rare case where the GUI actually requires less work than typing the requisite commands would. Changing the IDENTITY property is one thing DDL just hasn't caught up with, and the nonsense it has to do to work around it is ridiculous (in this case I added a new column, and scripted out the change required to make that the identity column instead - and you can't even see all the nonsense here, because of the non-resizable dialog):
Here is the actual script (not something you probably want to run very often, never mind come up with on your own):
/* To prevent any potential data loss issues, you should review this script in detail
before running it outside the context of the database designer.*/
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_foo
(
ID int NOT NULL,
[Foo INT] int NOT NULL IDENTITY (1, 1)
) ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_foo SET (LOCK_ESCALATION = TABLE)
GO
SET IDENTITY_INSERT dbo.Tmp_foo OFF
GO
IF EXISTS(SELECT * FROM dbo.foo) EXEC('INSERT INTO dbo.Tmp_foo (ID)
SELECT ID FROM dbo.foo WITH (HOLDLOCK TABLOCKX)')
GO
DROP TABLE dbo.foo
GO
EXECUTE sp_rename N'dbo.Tmp_foo', N'foo', 'OBJECT'
GO
ALTER TABLE dbo.foo ADD CONSTRAINT
PK__foo__3214EC274CF5691D PRIMARY KEY CLUSTERED
(ID) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
COMMIT
If you want to change this property in Management Studio, you right click on the table, select Design, and then select the primary key column, and see its properties in the bottom, where you can open the "Identity Specification" section. There, you can edit the setting "IsIdentity". Then save the changes.

SQLSERVER 2008 R2 Using filtered Indexes

Is it true, that QUOTED_IDENTIFIER is to be set to ON when using filtered Index ?
I know it has to be so when using indexed views or index on computed columns.
Yes - from the documentation:
Required SET Options for Filtered Indexes...
SET options Required value
QUOTED_IDENTIFIER ON
If the SET options are incorrect, the following conditions can occur:
The filtered index is not created.
The Database Engine generates an error and rolls back INSERT, UPDATE, DELETE, or MERGE statements that change data in the index.
Query optimizer does not consider the index in the execution plan for any Transact-SQL statements.