Can you update CHANGE_RETENTION on single table? - azure-sql-database

We have change tracking enabled on the database level with a retention period of 5 days. Is it possible to update a single table to 10 days?
If not what is the query to update the db. Microsoft doco gives this example:
ALTER DATABASE AdventureWorks2012
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)
If I run this the query breaks at line 2 since change tracking is already on

I think it is not able to alter CHANGE_RETENTION property directly. Before re-enable CHANGE_RETENTION property for the database, we need to disable CHANGE_RETENTION for the tables which have been enabled. Disable and enable commands can refer Enable and Disable Change Tracking (SQL Server).
Disable them:
ALTER TABLE <table1>
DISABLE CHANGE_TRACKING
ALTER TABLE <table2>
DISABLE CHANGE_TRACKING
...
ALTER DATABASE <DBName>
SET CHANGE_TRACKING = OFF
Then enable them:
ALTER DATABASE <DBName>
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 10 DAYS, AUTO_CLEANUP = ON)
ALTER TABLE <table1>
ENABLE CHANGE_TRACKING
ALTER TABLE <table2>
ENABLE CHANGE_TRACKING
...

Related

Adding an extra column and constraint but then when updating the column it fails

I am attempting to run the following query on SQL Server 2008 in SSMS, however it constantly fails on the third query stating:
Invalid column name 'hasCodeMappingDefaults'.
BEGIN
ALTER TABLE [SIntegrationProvider]
ADD [hasCodeMappingDefaults] BIT NULL;
END
BEGIN
ALTER TABLE [SIntegrationProvider]
ADD CONSTRAINT [DF_SIntegrationProvider_hasCodeMappingDefaults] DEFAULT ((1)) FOR [hasCodeMappingDefaults];
END
BEGIN
UPDATE [SIntegrationProvider]
SET [hasCodeMappingDefaults] = 1
WHERE [provider] = 'EmployeeNavigator';
END
When each query is run individually in order they work, I just cannot figure out why I cannot run all three combined (this needs to be done as part of a much larger script being rolled out).
Actually I think I may have found the problem, doing this seems to have resolved the issue:
ALTER TABLE [SIntegrationProvider]
ADD [hasCodeMappingDefaults] BIT NULL
GO
ALTER TABLE [SIntegrationProvider]
ADD CONSTRAINT [DF_SIntegrationProvider_hasCodeMappingDefaults] DEFAULT ((1)) FOR [hasCodeMappingDefaults]
GO
-- Set the new value for ENav
IF EXISTS(SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'SIntegrationProvider'
AND COLUMN_NAME = 'hasCodeMappingDefaults')
UPDATE [SIntegrationProvider]
SET [hasCodeMappingDefaults] = 1
WHERE [provider] = 'EmployeeNavigator';

Ad hoc updates to system catalogs are not allowed in SQL Server 2012

I want to remove identity from a column by updating it like this..
sp_configure 'allow update', 1
go
reconfigure with override
go
update sys.syscolumns
set colstat = 0 -- turn off bit 1 which indicates identity column
where id = object_id('tbl1') and name = 'ids'
go
sp_configure 'allow updates', 0
go
reconfigure
go
I am getting this error, tried many times.
Msg 259, Level 16, State 1, Line 15
Ad hoc updates to system catalogs are not allowed.
If you want to get rid of it completely, just rename the table and then dump the data into a new table.
EXEC sp_rename 'OriginalTblName','OLD_OriginalTblName'
CREATE TABLE OriginalTblName (Definition of your Table)
INSERT OriginalTblName
SELECT * FROM OLD_OriginalTblName
You can skip the CREATE TABLE step if you want by just selecting the contents into the new table. You lose the ability to define the fields the way you want with this method.
SELECT * FROM OLD_OriginalTblName
INTO OriginalTblName
If you are just wanting to INSERT new records, you can use IDENTITY INSERT to insert the records you want. Just be careful not to duplicate the values or you will break the table.
SET IDENTITY_INSERT ON OriginalTblName
INSERT OriginalTblName
SELECT someFields
FROM someTbl
SET IDENTITY_INSERT OFF OriginalTblName
IDENTITY INSERT will not work for UPDATE on the IDENTITY field. You will need to capture the data and reinsert the record with one of the methods described above.

Update ANSI_NULLS option in an existing table

In our database there is a table which is created with ANSI_NULLS OFF. Now we have created a view using this table. And we want to add a clustered index for this view.
While creating the clustered index it is showing an error like can't create an index since the ANSI_NULL is off for this particular table.
This table contains a large amount of data. So I want to change this option to ON without losing any data.
Is there any way to alter the table to modify this option . Please give your suggestions.
This was cross posted on Database Administrators so I might as well post my answer from there here too to help future searchers.
It can be done as a metadata only change (i.e. without migrating all the data to a new table) using ALTER TABLE ... SWITCH.
Example code below
/*Create table with option off*/
SET ANSI_NULLS OFF;
CREATE TABLE dbo.YourTable (X INT)
/*Add some data*/
INSERT INTO dbo.YourTable VALUES (1),(2),(3)
/*Confirm the bit is set to 0*/
SELECT uses_ansi_nulls, *
FROM sys.tables
WHERE object_id = object_id('dbo.YourTable')
GO
BEGIN TRY
BEGIN TRANSACTION;
/*Create new table with identical structure but option on*/
SET ANSI_NULLS ON;
CREATE TABLE dbo.YourTableNew (X INT)
/*Metadata only switch*/
ALTER TABLE dbo.YourTable SWITCH TO dbo.YourTableNew;
DROP TABLE dbo.YourTable;
EXECUTE sp_rename N'dbo.YourTableNew', N'YourTable','OBJECT';
/*Confirm the bit is set to 1*/
SELECT uses_ansi_nulls, *
FROM sys.tables
WHERE object_id = object_id('dbo.YourTable')
/*Data still there!*/
SELECT *
FROM dbo.YourTable
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK TRANSACTION;
PRINT ERROR_MESSAGE();
END CATCH;
WARNING: when your table contains an IDENTITY column you need to reseed the IDENTITY value.
The SWITCH TO will reset the seed of the identity column and if you do not have a UNIQUE or PRIMARY KEY constraint on the identity (e.g. when using CLUSTERED COLUMNSTORE index in SQL 2014) you won't notice it right away.
You need to use DBCC CHECKIDENT ('dbo.YourTable', RESEED, [reseed value]) to correctly set the seed value again.
Unfortunately, there is no way how to do it without recreating. You need to create new table with ANSI_NULLS ON and copy there all data.
It should be something like:
SET ANSI_NULLS ON;
CREATE TABLE new_MyTBL (
....
)
-- stop all processes changing your data at this point
SET IDENTITY_INSERT new_MyTBL ON
INSERT new_MyTBL (...) -- including IDENTITY field
SELECT ... -- including IDENTITY field
FROM MyTBL
SET IDENTITY_INSERT new_MyTBL OFF
-- alter/drop WITH SCHEMABINDING objects at this point
EXEC sp_rename #objname = 'MyTBL', #newname = 'old_MyTBL'
EXEC sp_rename #objname = 'new_MyTBL', #newname = 'MyTBL'
-- alter/create WITH SCHEMABINDING objects at this point
-- re-enable your processes
DROP TABLE old_MyTBL -- do that when you are sure that system works OK
If there are any depending objects, they will work with new table as soon as you rename it. But if some of them are WITH SCHEMABINDING you need to DROP and CREATE them manualy.
I tried the SWITCH option recommended above but was unable to RESEED the identity. I could not find out why.
I used the following alternative approach instead:
Create database snapshot for the database that contains the table
Script table definition of the table you intend to update
Delete the table that you intend to update (Make sure the database snapshot is successfully created)
Update SET ANSI NULLs from OFF to ON from the script obtained from step 2 and run updated script. Table is now recreated.
Populate data from database snapshot to your table:
SET IDENTITY_INSERT TABLE_NAME ON
INSERT INTO TABLE_NAME (PK, col1, etc.)
SELECT PK, col1, etc.
FROM [Database_Snapshot].dbo.TABLE_NAME
SET IDENTITY_INSERT TABLE_NAME OFF
Migrate non clustered index manually (get script from database snapshot)
Using the above:
I did not have to worry about constraints and keys since table/constraint names always remain the same (I do not need to rename anything)
I have a backup of my data (the snapshot) which I can rely on to double check that nothing is missing.
I do not need to reseed the identity
I realize deleting table may not always be straightforward if table is referenced in other tables. That was not the case for me in this instance.. I was lucky.

SQL Server Keys and Index Help

Im trying to use Keys and Indexson a database and having difficulty. My requirements are as follows:
I want to be able to have empty string values in the Document No column as well as values
The constraint should check for Unique Values based on a DatabaseID and DocumentNo ( eg you can have the same document no for 2 different database ID's)
The table is similar to this (Extra columns removed for simplicity)
RecordID (bigint)
DocumentNo (varchar(12))
DatabaseID (bigint)
So the constraint should not allow inserting or updating a record if there is already a document no for the specified document no and database ID. A blank document no should be allowed to be entered as there are multiple rows with no document no.
Is this possible? if so please could you let me know how.
EDIT:
Here is the Query to Create the View and Constraint:
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
CREATE VIEW vNoDuplicateDoNos
WITH SCHEMABINDING
AS
SELECT [PODocumentNo],[SageDatabaseID]
FROM dbo.[Order]
WHERE [PODocumentNo] <> ''
GO
--Create an index on the view.
CREATE UNIQUE CLUSTERED INDEX CI_V1_ID
ON vNoDuplicateDoNos ([PODocumentNo],[SageDatabaseID]);
GO
I am not sure about using the GUI for this, but you should use a UNIQUE INDEX with a WHERE condition (assuming you are using SQL Server 2008 or newer):
http://msdn.microsoft.com/en-us/library/ms188783.aspx
CREATE UNIQUE NONCLUSTERED INDEX UIX_TableName_DocumentNo_DatabaseID
ON dbo.TableName
(DocumentNo, DatabaseID)
WHERE DocumentNo <> ''
ON IndexesFileGroup -- omit this line if you do not have a File Group for Indexes
Also, I was not able to find an option for the WHERE condition via the GUI in the "Manage Indexes and Keys" dialog.
To be fair, this was suggested by "Martin" in a comment on the Question. I just felt it needed to be explicitly stated with an example.
IF you are using a version of SQL Server prior to 2008 (when Filtered Indexes were added), you can use a Trigger as follows:
CREATE TRIGGER dbo.TableName_PreventDuplicatesTrigger
ON dbo.TableName
AFTER INSERT, UPDATE
AS
SET NOCOUNT ON
IF (EXISTS(
SELECT 1
FROM dbo.TableName tn
INNER JOIN INSERTED ins
ON ins.DocumentNo = tn.DocumentNo
AND ins.DatabaseID = tn.DatabaseID
WHERE ins.DocumentNo <> ''
))
BEGIN
ROLLBACK TRAN
RAISERROR('Duplicate DocumentNo/DatabaseID combination detected!', 16, 1)
END
GO
The above trigger will look for any existing records that match the two fields but only if the inserted or updated DocumentNo is not empty. If found, it calls ROLLBACK which will cancel the INSERT or UPDATE statement and the RAISERROR will display a message as to what caused the ROLLBACK.
Another option when using a version of SQL Server prior to 2008 is to create an Indexed View on DocumentNo and DatabaseID while filtering out empty DocumentNo records. This was suggested by "Martin" in the comments to this Answer.
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
CREATE VIEW dbo.UniqueDocumentAndDatabase
WITH SCHEMABINDING
AS
SELECT DocumentNo, DatabaseID
FROM dbo.TableName
WHERE DocumentNo <> ''
GO
CREATE UNIQUE CLUSTERED INDEX UIX_UniqueDocumentAndDatabase
ON dbo.UniqueDocumentAndDatabase
(DocumentNo, DatabaseID)
GO
Please note that the two SET options as well as the WITH SCHEMABINDING are required for Indexed Views. It is also required that SET ANSI_NULLS ON was used when creating the base table.

MySQL conditional drop foreign keys script

I'm involved is a project to migrate a project from Oracle to MySQL. I Have a script that i'm running from the MySQL shell command, called CreateTables.sql that looks like this internally:
source table\DropForeignKeys.sql
source tables\Site.sql
source tables\Language.sql
source tables\Country.sql
source tables\Locale.sql
source tables\Tag.sql
mysql --user=root --password --database=junkdb -vv < CreateTables.sql
What I'm after is a way to make the execution for the first script DropForeignKeys.sql conditional based on if the db has any tables of not. Alternatively it would be nice if there were a way to drop constraint if not exists but such a construct does not exists in MySQL to my knowledge.
So my question is how do I make the dropping of foreign key constraints conditional at script level or constraint level, so that I can have a reliable re-playable script?
What I'm after is a way to make the execution for the first script DropForeignKeys.sql conditional based on if the db has any tables of not.
Conditional logic (IF/ELSE) is only supported in functions and stored procedures - you'd have to use something that resembles:
DELIMITER $$
DROP PROCEDURE IF EXISTS upgrade_database $$
CREATE PROCEDURE upgrade_database()
BEGIN
-- INSERT NEW RECORD IF PREEXISTING RECORD DOESNT EXIST
IF((SELECT COUNT(*) AS column_exists
FROM information_schema.columns
WHERE table_name = 'test'
AND column_name = 'test7') = 0) THEN
ALTER TABLE test ADD COLUMN `test7` int(10) NOT NULL;
UPDATE test SET test7 = test;
SELECT 'Altered!';
ELSE
SELECT 'Not altered!';
END IF;
END $$
DELIMITER ;
CALL upgrade_database();
Rather than reference INFORMATION_SCHEMA.COLUMNS, you could reference INFORMATION_SCHEMA.KEY_COLUMN_USAGE.
Depending on your needs:
ALTER TABLE [table name] DISABLE KEYS
ALTER TABLE [table name] ENABLE KEYS
...will disable and re-enable the keys attached to that table without needing to know each one. You can disable and enable keys on a database level using SET foreign_key_checks = 0; to disable, and SET foreign_key_checks = 1; to enable them.
It surprises me that MySQL doesn't seem to have a better way of dealing with this common scripting problem.
Oracle doesn't either, but constraints aren't really something you want to alter blindly without knowing details.
The reason I need the drop foreign keys script is because drop table yields an error when their are FK attachments. Will disabling FK checks allow for me to drop the tables?
Yes, dropping or disabling the constraints will allow you to drop the table but be aware - in order to re-enable the fk check you'll need the data in the parent to match the existing data in the child tables.