Can't disable change tracking on table via Sql Server Data Tools - sql-server

I have change tracking enabled/disabled in table [MyTable] of database [MyDb] using below given queries.
Pre Deployment query of SSDT project to disable change tracking on the table-
--Disable Change Tracking
IF EXISTS ( SELECT 1
FROM sys.change_tracking_tables
WHERE object_id = OBJECT_ID('MyTable'))
Begin
ALTER TABLE [dbo].[MyTable]
DISABLE CHANGE_TRACKING
End
GO
Declare #DBNAME SYSNAME = DB_NAME()
, #Sql NVARCHAR(MAX);
IF EXISTS (SELECT 1
FROM sys.change_tracking_databases
WHERE database_id = DB_ID(#DBNAME))
Begin
SET #Sql = N'ALTER DATABASE '+ QUOTENAME(#DBNAME) + N'
SET CHANGE_TRACKING = OFF WITH ROLLBACK IMMEDIATE'
Exec sp_executesql #Sql
End
GO
Post Deployment query of SSDT project to enable change tracking on the table-
--Enable Change Tracking
Declare #DBNAME SYSNAME = DB_NAME()
, #Sql NVARCHAR(MAX);
IF NOT EXISTS (SELECT 1
FROM sys.change_tracking_databases
WHERE database_id = DB_ID(#DBNAME))
Begin
SET #Sql = N' ALTER DATABASE '+ QUOTENAME(#DBNAME) + N'
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON) '
Exec sp_executesql #Sql
End
GO
IF NOT EXISTS ( SELECT 1
FROM sys.change_tracking_tables
WHERE object_id = OBJECT_ID('MyTable'))
Begin
ALTER TABLE [dbo].[MyTable]
ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON)
End
GO
When I publish the SSDT project I got below given error in the publish result-
Altering Table [dbo].[MyTable]...
(114,1): SQL72014: .Net SqlClient Data Provider: Msg 4998, Level 16, State 1, Line 1 Change tracking is not enabled on table 'MyTable'.
(114,0): SQL72045: Script execution error. The executed script:
ALTER TABLE [dbo].[MyTable] DISABLE CHANGE_TRACKING;
An error occurred while the batch was being executed.
Reviewing the publish script I found that SSDT has already added a has section to disable change tracking at the database level in the start of publish script.
IF EXISTS (SELECT 1
FROM [sys].[databases]
WHERE [name] = N'$(DatabaseName)')
BEGIN
ALTER DATABASE [$(DatabaseName)]
SET QUERY_STORE = OFF
WITH ROLLBACK IMMEDIATE;
END
GO
But as I know that without disabling change tracking on table level we cannot disable it on database level.
I stuck here that how can I remove the default change tracking provided by SSDT itself.

Related

SQL Server 2014 Create DDL trigger on Create Table that creates a trigger for the table

I'm working with a third party tool that creates databases and tables. I would like a trigger on one of those tables being created. I thought I would try to create a server DDL trigger that fires when the table is created in a new database, which in turn creates a trigger on that table. I cannot add the trigger to the 'model' database because this table is created via the tool dynamically.
I've tried the following:
CREATE TRIGGER ddl_trig_createTable
ON ALL SERVER
FOR CREATE_TABLE
AS
DECLARE #databaseName varchar(255)
DECLARE #AffectedTables varchar(255)
SELECT #AffectedTables = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(100)')
IF (#AffectedTables IN ('DynamicallyCreatedTable'))
BEGIN
select #databaseName = CAST(eventdata().query('/EVENT_INSTANCE/DatabaseName[1]/text()') as NVarchar(128))
EXEC('CREATE TRIGGER ' + #databaseName + '.[dbo].[tgrDynamicTableTrigger]
ON
' + #databaseName + '.[dbo].[DynamicallyCreatedTable]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
-- trigger code here
END')
END
GO
Which produces the following error when the table is created;
Msg 166, Level 15, State 1, Line 1 'CREATE/ALTER TRIGGER' does not
allow specifying the database name as a prefix to the object name.
I tried changing the dynamic sql by replacing the fully qualified table name to attempt a 'use' statement:
--- 8< ---
EXEC('use ' + #databaseName + '
CREATE TRIGGER [dbo].[tgrDynamicTableTrigger]
ON
--- 8< ---
However, that produced the following error when the table was created:
Msg 111, Level 15, State 1, Line 2 'CREATE TRIGGER' must be the first
statement in a query batch.
Any ideas?
I'm using SQL Server 2014.
I believe that I have figured it out, thanks mostly to this answer.
Here's the code:
CREATE TRIGGER ddl_trig_createTable
ON ALL SERVER
FOR CREATE_TABLE
AS
DECLARE #statement nvarchar(max) = 'CREATE TRIGGER [dbo].[tgrDynamicTableTrigger]
ON
[dbo].[DynamicallyCreatedTable]
AFTER UPDATE
AS
BEGIN
-- trigger code here
END'
DECLARE #databaseName varchar(255)
DECLARE #AffectedTables varchar(255)
SELECT #AffectedTables = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(100)')
IF (#AffectedTables IN ('DynamicallyCreatedTable'))
BEGIN
SET #databaseName = CAST(eventdata().query('/EVENT_INSTANCE/DatabaseName[1]/text()') as NVarchar(128))
DECLARE #sql NVARCHAR(MAX) = QUOTENAME(#databaseName) + '.sys.sp_executesql';
EXEC #sql #statement;
END
GO

SQL Server 2016 Change Object Owner

I've inherited a SQL 2008 dbase in which all of its objects are prefixed with the name of the developer as owner, i.e. ownername.sp_get_all_users.
I've restored the dbase onto SQL Server 2016 Express Edition.
There are several hundred dbase objects, is there a way to automate changing the object owners to dbo rather than manually editing each object?
I've tried the following but apparently you can no longer make ad-hoc changes to objects since SQL Server 2005?
SELECT * from sysobjects where uid = user_id('UseNAme')
declare #Return int
exec #Return = sp_configure 'allow updates', '1'
SELECT #Return as 'Returned Code'
GO
reconfigure WITH OVERRIDE
GO
DECLARE #Rows int, #Error int
BEGIN TRANSACTION
update sysobjects set uid = user_id('dbo') where uid = user_id('UseNAme')
SELECT #Error = ##Error, #Rows = ##RowCount
SELECT #Rows as '#Rows'
IF #Rows > 0
BEGIN
SELECT #Rows AS '#Rows'
COMMIT TRANSACTION
END
else
BEGIN
SELECT #Error AS 'Error #'
ROLLBACK TRANSACTION
END
exec sp_configure 'allow updates', '0'
reconfigure WITH OVERRIDE
go
Any help most appreciated.
You have to use Alter Schema...
ALTER SCHEMA oldschemaname TRANSFER dbo.Address;
To Automate use below
this will change all tables which have a schema other than system to dbo,note if you have two tables in different schema,they can't exist in same schema
select *,row_number() over (order by (select null)) as rownum
into #tables
from information_Schema.tables
where table_schema in (select name from sys.schemas
where name not in ('dbo','guest','INFORMATION_SCHEMA','sys') and principal_id <16384
)
now move
declare #min int,#max int
select #min=min(rownum),#max=max(rownum)
from #tables
declare #tblname varchar(255),#schemaname sysname
declare #sql varchar(max)
while #min<=#max
Begin
select #tblname=table_name,#schemaname=table_schema from
#tables where rownum=#min
set #sql='alter schema dbo transfer '+ #schemaname+'.'+#tblname
--print #sql
exec(#sql)
Set #min=#min+1
End
sp_change object owner as per documentation states..
This stored procedure only works with the objects available in MicrosoftSQL Server 2000. This feature will be removed in a future version of Microsoft SQL Server. Avoid using this feature in new development work, and plan to modify applications that currently use this feature. Use ALTER SCHEMA or ALTER AUTHORIZATION instead. sp_changeobjectowner changes both the schema and the owner. To preserve compatibility with earlier versions of SQL Server, this stored procedure will only change object owners when both the current owner and the new owner own schemas that have the same name as their database user names.
Use this sp_changeobjectowner
As explained here MSDN
For example: EXEC sp_changeobjectowner 'YourObject', 'dbo'
You can use this to alter schema statement for newer SQL Server DBS
declare #sql varchar(8000), #table varchar(1000), #oldschema varchar(1000), #newschema varchar(1000)
set #oldschema = 'dbo'
set #newschema = 'exe'
while exists(select * from sys.tables where schema_name(schema_id) = #oldschema)
begin
select #table = name from sys.tables
where object_id in(select min(object_id) from sys.tables where schema_name(schema_id) = #oldschema)
set #sql = 'alter schema ' + #newschema + ' transfer ' + #oldschema + '.' + #table
exec(#sql)
end
Your general idea of looping through the objects owned by the developer is a good idea (assuming you've tested the heck out of it). I'd suggest using the ALTER AUTHORIZATION command instead MSDN Doc
In addition to the advice above, the following changes the owner of SPs:
Declare #sql varchar(8000),
#table varchar(1000),
#oldschema varchar(1000),
#newschema varchar(1000)
set #oldschema = 'developername'
set #newschema = 'dbo'
while exists(select * from information_schema.routines where routine_type = 'PROCEDURE' and routine_schema = #oldschema )
begin
select #table = SPECIFIC_NAME from information_schema.routines
where SPECIFIC_NAME in(select SPECIFIC_NAME from information_schema.routines where routine_type = 'PROCEDURE' and routine_schema = #oldschema)
set #sql = 'alter schema ' + #newschema + ' transfer ' + #oldschema + '.' + #table
exec(#sql)
end

Disabling Index operations for certain tables in SQL Server 2008 R2 other than DDL trigger

Is there a way to disable certian index DDL operation (create, drop and alter index) for a list of tables in MS SQL Server 2008 R2?
What I was trying to do is to create a DDL trigger that catch these events and roll them back, but it seems that all ddl trigers are after triggers and if table is very large this cause performance issues.
The trigger I am currently using is the following:
CREATE TRIGGER index_guard
ON DATABASE
FOR CREATE_INDEX, DROP_INDEX, ALTER_INDEX
AS
DECLARE #object_name NVARCHAR(50);
DECLARE #table_name NVARCHAR(50);
DECLARE #target_object_type NVARCHAR(20);
DECLARE #object_type NVARCHAR(20);
DECLARE #lookup_value NVARCHAR(100);
DECLARE #protected_indexes TABLE (Name NVARCHAR(50))
INSERT INTO #protected_indexes
SELECT Name FROM (VALUES ('TABLE1/IX_IdName'), ('TABLE2/IX_NameId')) AS tbl(Name)
SELECT #object_name = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(max)');
SELECT #table_name = EVENTDATA().value('(/EVENT_INSTANCE/TargetObjectName)[1]','nvarchar(max)');
SELECT #target_object_type = EVENTDATA().value('(/EVENT_INSTANCE/TargetObjectType)[1]','nvarchar(max)');
SELECT #object_type = EVENTDATA().value('(/EVENT_INSTANCE/ObjectType)[1]','nvarchar(max)');
IF #object_type = 'INDEX' AND #target_object_type = 'TABLE'
BEGIN
SET #lookup_value = #table_name + '/' + #object_name
IF EXISTS (SELECT 1 FROM #protected_indexes A WHERE A.Name = #lookup_value)
BEGIN
ROLLBACK
END
END

Switching database context at run time

I am using below code to switch db context to master and create procedure and setup start up script.
BEGIN TRY
DECLARE #dbName NVARCHAR(100)
SET #dbName = DB_NAME()
USE MASTER
IF NOT EXISTS (
SELECT name
FROM sys.objects
WHERE object_id = OBJECT_ID('spSetTrustWorthyOn')
)
EXEC (
'CREATE PROCEDURE spSetTrustWorthyOn
AS
BEGIN
ALTER DATABASE [' + #dbName + '] SET TRUSTWORTHY ON
END'
)
EXECUTE sp_procoption 'spSetTrustWorthyOn'
,'startup'
,'ON'
END TRY
BEGIN CATCH
END CATCH
GO
Now Issue is when I want to switch back to existing database.I could not find any way to go back to my original database.
I also can not hard code the database as this is dynamic query and we have multiple databases.
Any help will be much appreciated.
Thanks
Instead of a USE statement for the master database, qualify the catalog views and use EXEC sp_executesql statement with the master database qualified. This will avoid changing the database context in the outer script.
DECLARE
#dbName sysname = DB_NAME()
,#sql nvarchar(MAX);
BEGIN TRY
IF NOT EXISTS (
SELECT *
FROM master.sys.objects
WHERE object_id = OBJECT_ID(N'spSetTrustWorthyOn')
)
BEGIN
SET #sql = N'CREATE PROCEDURE spSetTrustWorthyOn
AS
BEGIN
ALTER DATABASE ' + QUOTENAME(#dbName) + ' SET TRUSTWORTHY ON;
END;';
EXECUTE master..sp_executesql #sql;
EXECUTE sp_procoption
'spSetTrustWorthyOn'
,'startup'
,'ON';
END;
END TRY
BEGIN CATCH
THROW;
END CATCH;
GO

Dropping and recreating databases in Microsoft SQL Server

I am experimenting and learning with Microsoft SQL Server 2008 R2 SP1.
I have a database where I made many experiments. Now I would like to drop and recreate it.
So I extract the creation script from database, I delete it and I use the script to recreate it.
To my surprise, all the tables, keys etc are still there.
How do I drop the database, so that I can rebuild the database from scratch?
USE master
IF EXISTS(select * from sys.databases where name='yourDBname')
DROP DATABASE yourDBname
CREATE DATABASE yourDBname
+1 to AnandPhadke for his part of the code
This code will close all active connections to the database and then drop it
WHILE EXISTS(select NULL from sys.databases where name='YourDBName')
BEGIN
DECLARE #SQL varchar(max)
SELECT #SQL = COALESCE(#SQL,'') + 'Kill ' + Convert(varchar, SPId) + ';'
FROM MASTER..SysProcesses
WHERE DBId = DB_ID(N'YourDBName') AND SPId <> ##SPId
EXEC(#SQL)
DROP DATABASE [YourDBName]
END
GO
CREATE DATABASE YourDBName
GO
SQL Server 2016 (and above) support one line and atomic(?) syntax DROP DATABASE IF EXISTS database_name
REF: https://msdn.microsoft.com/en-us/library/ms178613.aspx
Requiring the DBName to be typed more than once is error prone, at some point it'll be executed with inconsistent entries and unintended consequences.
The answers from AnandPhadke or Pierre with variable support would be preferred for me.
DECLARE #DBName varchar(50) = 'YourDatabaseName'
USE master
IF EXISTS(select * from sys.databases where name= #DBName)
EXEC('DROP DATABASE ' + #DBName)
EXEC('CREATE DATABASE ' + #DBName)
or
DECLARE #DBName varchar(50) = 'YourDatabaseName'
WHILE EXISTS(select NULL from sys.databases where name = #DBName )
BEGIN
DECLARE #SQL varchar(max)
SELECT #SQL = COALESCE(#SQL,'') + 'Kill ' + Convert(varchar, SPId) + ';' FROM MASTER..SysProcesses WHERE DBId = DB_ID(#DBName) AND SPId <> ##SPId
EXEC(#SQL)
EXEC('DROP DATABASE ' + #DBName)
END
GO
I extract the creation script from database
This extract the creation script for everything in the database (tables, keys etc). If you simply want to create an empty database, just run CREATE DATABASE <dbname>
This works best for me:
if exists (select name from sys.databases where name='YourDBName')
alter database YourDBName set single_user with rollback immediate
go
if exists (select name from sys.databases where name='YourDBName')
drop database YourDBName

Resources