Is there any simple way to create a column in MS SQL that will track the last time a record was updated?
I would like to have two fields. One to track when the record was created. That one is simple. Create a datetime field and set its default to getdate(). However the second field seams to be a bit more tricky. I want it to have the latest date (and time) the record was modified.
My options are:
Include getdate() in every update statement - not an option, these tables will be accessed from MS Access
Allow updates only through an SP. - not an option, these tables will be accessed from MS Access
Create triggers for each table - the DB is recreated on many machines and I am afraid it will conflict or be forgotten or get out of synch.
Are there any other options?
Triggers are pretty much your only option here. What is to stop anyone from updating tables with SSMS, those updates would not update the date updated column in that case
Option 4:
Create a stored procedure that automatically creates triggers for all the tables in your database. In SQL 2005, optionally run this trigger any time any table is created (using a DDL trigger).
CREATE PROC UpdateTriggersCreate
AS
DECLARE
#TableSchema sysname,
#TableName sysname,
#PrimaryKeys nvarchar(4000),
#ObjectName nvarchar(4000)
#TriggerName nvarchar(4000),
#SQL nvarchar(4000);
SET #TableName = '';
SET #TableSchema = '';
WHILE 1 = 1 BEGIN
SELECT TOP 1
#TableSchema = TABLE_SCHEMA,
#TableName = TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
COLUMN_NAME = 'LastUpdatedDate'
AND (
TABLE_SCHEMA > #TableSchema
OR (
TABLE_SCHEMA = #TableSchema
AND TABLE_NAME > #TableName
)
)
ORDER BY TABLE_SCHEMA, TABLE_NAME;
IF ##RowCount = 0 BREAK;
IF NOT EXISTS (
SELECT *
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
WHERE
C.CONSTRAINT_TYPE = 'PRIMARY KEY'
AND C.TABLE_SCHEMA = #TableSchema
AND C.TABLE_NAME = #TableName
) BEGIN
PRINT '-- Not processing table ''' + #TableSchema + '.' + #TableName + ''' because automatic last updated triggers cannot be used on tables with no primary key.';
CONTINUE;
END;
SET #PrimaryKeys = NULL;
SELECT #PrimaryKeys = Coalesce(#PrimaryKeys + ' AND T.', 'T.') + QuoteName(Y.COLUMN_NAME) + ' = I.' + QuoteName(Y.COLUMN_NAME)
FROM
INFORMATION_SCHEMA.TABLE_CONSTRAINTS T
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE Y
ON T.CONSTRAINT_CATALOG = Y.CONSTRAINT_CATALOG
AND T.CONSTRAINT_SCHEMA = Y.CONSTRAINT_SCHEMA
AND T.CONSTRAINT_NAME = Y.CONSTRAINT_NAME
WHERE
T.CONSTRAINT_TYPE = 'PRIMARY KEY'
AND T.TABLE_SCHEMA = #TableSchema
AND T.TABLE_NAME = #TableName;
-- order is not important which is good because ORDER BY is unreliable in this case
SET #ObjectName = #TableSchema + '.' + #TableName;
SET #TriggerName = 'TR_' + Replace(#ObjectName, '.', '_') + '_U_TimeUpdated';
SET #SQL = 'IF Object_ID(''' + #TriggerName + ''', ''TR'') IS NOT NULL DROP TRIGGER ' + #TriggerName;
EXEC sp_executesql #SQL;
SET #SQL = 'CREATE TRIGGER ' + #TriggerName + ' ON ' + #ObjectName + ' FOR INSERT
AS
SET NOCOUNT ON
UPDATE T
SET T.LastUpdatedDate = GetDate()
FROM
' + #ObjectName + ' T
INNER JOIN Inserted I ON ' + #PrimaryKeys;
EXEC sp_executesql #SQL;
END;
Once you have a stored procedure like this, schedule it to run once a day or (in sql 2005 and up) in response to the DDL creation of tables.
Update 1
The code now handles schema properly, and looks up the primary keys. It also reports on and skips tables that have no primary key.
I'm not sure if I worked out all the syntax errors--I adapted it from code I've done this in before and didn't actually test it. I'm sure you can figure it out.
Stored procs are an option with Access, but you have to intercept the event in VBA and call a stored proc instead, followed by Me.Undo.
I've done it but many moons ago and don't have sample code to show you, sorry.
Otherwise triggers are the usual way, either before or after ones.
Related
Here the #Data has a value with apostophe(')s . how do i update or insert a data based on the data value which is having apostophe in a dynamic sql
suppose #data has one value abc and another value abc's it throwing error for the second one
SET #SQL = ' Update '+ #ProcessCode + '_abc SET IS_IGNORING = 1 where Column_Name = '''+ #Column_Name +''' and [DATA] = ''' + #Data + ''' and Table_name = '''+ #Table_Name + ''''
Generally what i found is a manual process of adding one more apostophe but i am not really sure how to use that in a dynamic sql where not all data in the table is same, few of the data records has got this type of apostophe(')
Parameterized your query using sp_executesql
Example:
SET #SQL = 'Update ' + #ProcessCode + '_abc '
+ 'SET IS_IGNORING = 1 '
+ 'where Column_Name = #Column_Name '
+ 'and [DATA] = #Data '
+ 'and Table_name = #Table_Name '
EXEC sp_executesql #SQL,
N'#Column_Name varchar(100), #Data varchar(100), #Table_Name varchar(100)',
#Column_Name, #Data, #Table_Name
Do read up more on dynamic query and SQL Injection
You might find convenient to use parameterized queries, so you can replace static values with placeholders and then bind values to those placeholders before executing the query. It has certain advantages like better performance and helps to avoid SQL-injection attacks.
More info here: https://techcommunity.microsoft.com/t5/sql-server-blog/how-and-why-to-use-parameterized-queries/ba-p/383483
I am trying to set a default value to a column(Inserted_time), but first i need to check if the column exists in the tables. If the column doesn't exist, I need to add that column and give it a default value.
I am working with Sql Server Management Studio.
So far I have written this code:
IF EXISTS ( select TABLE_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_CATALOG = 'DB_COPY' and COLUMN_NAME = 'Inserted_Time')
begin
ALTER TABLE table_name ADD CONSTRAINT [Inserted_Time_Def] SET DEFAULT (sysdatetimeoffset()) FOR [Inserted_Time]
end
else
ALTER TABLE table_name ADD COLUMN [Inserted_Time] CONSTRAINT [Inserted_Time_Def] DEFAULT (sysdatetimeoffset()) WITH VALUES
Once I retrieve the tables that has the column, I need to add that table_name to the Alter command. But I am not able to do that. Can someone please tell me how to use the table_names retrieved from select statement in the alter statement?
First, you want to put all the table names in a temporary table so you can loop through it.
After, you can use a cursor to execute a command for each table name.
In my example, I only printed the command I wanted to execute. That way you can be sure the code will do what you want first.
Example :
select TABLE_NAME As TableName INTO #TablesList from INFORMATION_SCHEMA.COLUMNS where TABLE_CATALOG = 'DB_COPY' and COLUMN_NAME = 'Inserted_Time'
DECLARE #TablesCursor as CURSOR;
DECLARE #TableName as NVARCHAR(max);
DECLARE #CommandToExecute as NVARCHAR(max);
SET #TablesCursor = CURSOR FOR SELECT TableName FROM #TablesList;
OPEN #TablesCursor;
FETCH NEXT FROM #TablesCursor INTO #TableName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #CommandToExecute = 'ALTER TABLE ' + #TableName + ' WHAT YOU WANNA DO '
PRINT #CommandToExecute
--EXEC(#CommandToExecute)
FETCH NEXT FROM #TablesCursor INTO #TableName;
END
CLOSE #TablesCursor;
DEALLOCATE #TablesCursor;
Assuming that every table is in a different schema, then you could do something like this:
DECLARE #SQL nvarchar(MAX);
SET #SQL = STUFF((SELECT NCHAR(13) + NCHAR(10) +
CASE WHEN EXISTS (SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE T.TABLE_SCHEMA = C.TABLE_SCHEMA
AND T.TABLE_NAME = C.TABLE_SCHEMA
AND C.COLUMN_NAME = N'Inserted_Time') THEN N'ALTER TABLE ' + QUOTENAME(T.TABLE_SCHEMA) + N'.' + QUOTENAME(T.TABLE_NAME) + N' ADD CONSTRAINT [Inserted_Time_Def] DEFAULT (sysdatetimeoffset()) FOR [Inserted_Time];'
ELSE N'ALTER TABLE ' + QUOTENAME(T.TABLE_SCHEMA) + N'.' + QUOTENAME(T.TABLE_NAME) + N' ADD COLUMN [Inserted_Time] CONSTRAINT [Inserted_Time_Def] DEFAULT (sysdatetimeoffset());'
END
FROM INFORMATION_SCHEMA.TABLES T
WHERE T.TABLE_CATALOG = N'DB_COPY'
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,2,N'');
PRINT #SQL; --Your best friend. If more than 4,000 characters, use SELECT
EXECUTE sp_executesql #SQL;
This will very likely hugely out perform a CURSOR solution if you have a large number of schemas.
I currently have a trigger on each table that handles a history log. The trigger is the exact same on every table. See below.
If I move this to a stored procedure, will it be faster?
Also if I use a stored procedure will the trigger release for the user to continue?
create trigger ' + #TABLE_NAME + '_ChangeTracking on ' + #TABLE_NAME + ' for
insert, update, delete
as
declare #bit int ,
#field int ,
#maxfield int ,
#char int ,
#fieldname varchar(128) ,
#TableName varchar(128) ,
#PKCols varchar(1000) ,
#sql nvarchar(max),
#Type nvarchar(1) ,
#PKValueSelect varchar(1000),
#MasterId nvarchar(max) = ''0''
select #TableName = ''' + #TABLE_NAME + '''
if exists(select * from CNF_HIL_Tables where referencetable = #TableName and Active = 1)
begin
if exists (select * from inserted)
if exists (select * from deleted)
select #Type = ''2''
else
select #Type = ''3''
else
select #Type = ''1''
select * into #ins from inserted
select * into #del from deleted
select #PKCols = coalesce(#PKCols + '' and'', '' on'') + '' i.'' + c.COLUMN_NAME + '' = d.'' + c.COLUMN_NAME
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE c on c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME and c.TABLE_NAME = pk.TABLE_NAME
where pk.TABLE_NAME = #TableName
and CONSTRAINT_TYPE = ''PRIMARY KEY''
select #PKValueSelect = coalesce(#PKValueSelect+''+'','''') + ''convert(varchar(100), coalesce(i.'' + COLUMN_NAME + '',d.'' + COLUMN_NAME + ''))''
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE c on c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME and c.TABLE_NAME = pk.TABLE_NAME
where pk.TABLE_NAME = #TableName
and CONSTRAINT_TYPE = ''PRIMARY KEY''
select #field = 0,
#maxfield = max(ORDINAL_POSITION)
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = #TableName
while #field < #maxfield
begin
select #field = min(ORDINAL_POSITION)
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = #TableName
and ORDINAL_POSITION > #field
select #bit = (#field - 1 )% 8 + 1
select #bit = power(2,#bit - 1)
select #char = ((#field - 1) / 8) + 1
if substring(COLUMNS_UPDATED(),#char, 1) & #bit > 0 or #Type in (''1'',''3'')
begin
select #fieldname = COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = #TableName and ORDINAL_POSITION = #field
if exists(select * from CNF_Hil_Columns INNER JOIN CNF_HIL_Tables ON CNF_HIL_Tables.TablesId = CNF_Hil_Columns.TablesId
where CNF_HIL_Tables.referencetable = #TableName and CNF_Hil_Columns.ColumnName = #fieldname
and CNF_Hil_Columns.Active = 1
)
begin
if #MasterId = 0
begin
select #sql = ''insert DATA_HIL_Master (OperationType, ReferenceTable, ReferenceId, UserId, WorkstationId, InsDateTime)''
select #sql = #sql + '' select '''''' + #Type + ''''''''
select #sql = #sql + '', '''''' + #TableName + ''''''''
select #sql = #sql + '','' + #PKValueSelect
select #sql = #sql + '',convert(varchar(1000),i.Last_UserId_Log)''
select #sql = #sql + '',convert(varchar(1000),i.Last_WorkstationId_Log)''
select #sql = #sql + '',convert(varchar(1000),i.Last_DateTime_Log)''
select #sql = #sql + '' from #ins i full outer join #del d''
select #sql = #sql + #PKCols
select #sql = #sql + '' SELECT #MasterId = SCOPE_IDENTITY() ''
EXECUTE sp_executesql #sql, N''#MasterId nvarchar(max) OUTPUT'', #MasterId OUTPUT
end
select #sql = ''insert data_HIL_Detail (MasterId, ColumnName, OriginalValue, ModifiedValue)''
select #sql = #sql + '' select convert(varchar(1000),'' + #MasterId + '')''
select #sql = #sql + '','''''' + #fieldname + ''''''''
select #sql = #sql + '', convert(varchar(1000),d.'' + #fieldname + '')''
select #sql = #sql + '', convert(varchar(1000),i.'' + #fieldname + '')''
select #sql = #sql + '' from #ins i full outer join #del d''
select #sql = #sql + #PKCols
EXECUTE sp_executesql #sql
END
END
END
END
I was actually searching for an answer to this question and stumbled in here.
I've found a lot of different answers, but as a student, I am currently being told that "stored procedures run quicker than individual SQL statements; this improves performance." So, it seems the answer is "yes".
However, it seems "performance" may be interpreted differently by different people. I'm not very experienced yet, so I don't really understand all the nuances yet. I've seen some comments attributing the difference to "cache", and others that suggest using stored procedure only because of better "control" for security and maintenance rather than anything performance related.
While reading my course material, I also came across something that might be relevant. This is from Beginning Databases with PostgreSQL: From Novice to Professional (Stones and Matthew, 2005):
Stored Procedures reside on the server side, not on the client side, adding to
access control. Invoked from the client side, only the results are passed on to the caller, this reduces network traffic. Several applications can use a single stored procedure, standardizing processing rules.
So, maybe that's what is meant by "performance".
Stored procedures also seem more similar to functions themselves, which are objects stored in a database and used by all other database objects. Whereas triggers are object associated with a table that runs a function.
Generally, Irrespective of trigger or stored procedure, you have got the same code. In trigger, you can not call it directly and in stored procedure, you are calling directly. So, whether you use trigger or stored procedure, execution wise it is same. The first time it is called, the execution plan is cached.
In your case, as you are using specifically using inserted, deleted tables, you should have different stored procedure code to implement auditing. Or you can consider using SQL Server temporal tables or Change Data Capture or SQL Server auditing
But, there are few disadvantages of using trigger.
It can make the transaction longer
Difficult to debug
I'm trying a very simple drop column statement:
alter table MyTable drop column MyColumn
and receiving several errors along the lines of
Msg 5074, Level 16, State 1, Line 1
The statistics '_dta_stat_1268251623_3_2' is dependent on column 'MyColumn'.
followed ultimately by
Msg 4922, Level 16, State 9, Line 1
ALTER TABLE DROP COLUMN MyColumn failed because one or more objects access this column.
I didn't think statistics prevent a column from being dropped. Do they? If so, since these are apparently auto-created statistics I can't depend on the names being the same across multiple copies of the same database, so how could I drop all such statistics in an upgrade script to be executed on a different database?
The code proposed in JNK answer does not work, but the idea is good. If you want to delete all user created statistics this my tested solution :
DECLARE #sql NVARCHAR(MAX)
DECLARE statCursor CURSOR FOR
SELECT
'DROP STATISTICS ' + QUOTENAME(SCHEMA_NAME(t.schema_id))
+ '.' + QUOTENAME(t.name)
+ '.' + QUOTENAME(st.name) AS sql
FROM
sys.stats AS st
INNER JOIN sys.tables AS t
ON st.object_id = t.object_id
WHERE
st.user_created = 1
ORDER BY 1;
OPEN statCursor;
FETCH NEXT FROM statCursor INTO #sql
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #sql
EXEC sp_executesql #sql
FETCH NEXT FROM statCursor INTO #sql
END
CLOSE statCursor
DEALLOCATE statCursor
Auto-generated statistics that I have seen all either have the name of the index they represent OR start with something like WA_Sys_.
Are you 100% sure this is not a set of custom stats someone set up?
Check this:
select *
FROM sys.stats WHERE name = '_dta_stat_1268251623_3_2'
...and see what the user_created field indicates.
Per comment:
This is untested but you could try something like:
exec sp_MSforeachdb '
use ?
DECLARE #SQL varchar(max) = ''''
select #SQL = #SQL + ''DROP STATISTICS '' + OBJECT_NAME(c.object_id) + ''.'' + s.name + CHAR(10) + CHAR(13)
from sys.stats s
INNER JOIN sys.stats_columns sc
ON sc.stats_id = s.stats_id
INNER JOIN sys.columns c
ON c.column_id = sc.column_id
WHERE c.name = ''ClaimNbr''
--and s.user_created = 1
PRINT #SQL'
Change the PRINT to an EXEC if it looks good.
sp_msforeachdb is a cursor in the background but the rest of the logic you can do as a set.
[This is a bit of an unusual problem, I know...]
What I need is a script that will change every unique id value to new one in our database. The problem is that we have configuration tables that can be exported between instances of our software which is id-sensitive (clobbering existing ids). Years ago, we set up a "wide-enough" id gap between our development "standard configuration" and our client's instances, which is now not wide enough :( - e.g. we're getting id conflicts when clients import our standard configuration.
A SQL script to do the following is definitely the simplest/shortest-timeframe thing that we can do. e.g. fixing the code is far too complicated and error prone to consider. Note that we are not "eliminating" the problem here. Just changing the gap from 1000's to 1000000's or more (the existing gap took 5 years to fill).
I believe the simplest solution would be to:
change all our tables to UPDATE_CASCADE (none of them are - this will greatly simplify the script)
create an identity table with the new lowest id that we want
For each table, modify the id to the next one in the identity table (using identity insert modifier flags where necessary). Perhaps after each table is processed, we could reset the identity table.
turn off UPDATE_CASCADE, and delete the identity table.
I am seeking any (partial or full) scripts for this.
Unfortunately UPDATE_CASCADE doesn't exist in the world of Sql Server. I suggest for each table you to re-key you do the following (Pseudo Code)
BACKUP DATABASE
CHECK BACKUP WORKS!
FOR EACH TABLE TO BE RE-KEYED
DROP ALL FOREIGN KEY CONSTRAINTS, INDEXES ETC FROM TABLE
SELECT ID + Number, ALL_OTHER_FIELDS INTO TEMP_TABLE FROM TABLE
RENAME TABLE OLD_TABLE
RENAME TEMP_TABLE TABLE
FOR ALL TABLES REFERENCING THIS TABLE
UPDATE FOREIGN_KEY_TABLE SET FK_ID = FK_ID + new number
END FOR
RE-APPLY FOREIGN KEY CONSTRAINTS, INDEXES ETC FROM TABLE
END FOR
Check it all still works ...
This process could be automated through DMO/SMO objects, but depending on the number of tables involved I'd say using management studio to generate scripts that can then be edited is probably quicker. After all, you only need to do this once/5 years.
Here we go with the code for SQL 2005. It's huge, it's hacky, but it will work (except in the case where you have a primary key that is a composite of two other primary keys).
If someone can re-write this with MrTelly's faster id addition (which wouldn't require building sql from a cursor for each updated row), then I'll mark that as the accepted answer. (If I don't notice the new answer, upvote this - then I'll notice :))
BEGIN TRAN
SET NOCOUNT ON;
DECLARE #newLowId INT
SET #newLowId = 1000000
DECLARE #sql VARCHAR(4000)
--**** SELECT ALL TABLES WITH IDENTITY COLUMNS ****
DECLARE tables SCROLL CURSOR
FOR
SELECT '[' + SCHEMA_NAME(schema_id) + '].[' + t.name + ']', c.name
FROM sys.identity_columns c
INNER JOIN sys.objects t
on c.object_id = t.object_id
WHERE t.type_Desc = 'USER_TABLE'
OPEN tables
DECLARE #Table VARCHAR(100)
DECLARE #IdColumn VARCHAR(100)
CREATE Table #IdTable(
id INT IDENTITY(1,1),
s CHAR(1)
)
FETCH FIRST FROM tables
INTO #Table, #IdColumn
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT('
****************** '+#Table+' ******************
')
--Reset the idtable to the 'low' id mark - remove this line if you want all records to have distinct ids across the database
DELETE FROM #IdTable
DBCC CHECKIDENT('#IdTable', RESEED, #newLowId)
--**** GENERATE COLUMN SQL (for inserts and deletes - updating identities is not allowed) ****
DECLARE tableColumns CURSOR FOR
SELECT column_name FROM information_schema.columns
WHERE '[' + table_schema + '].[' + table_name + ']' = #Table
AND column_name <> #IdColumn
OPEN tableColumns
DECLARE #columnName VARCHAR(100)
DECLARE #columns VARCHAR(4000)
SET #columns = ''
FETCH NEXT FROM tableColumns INTO #columnName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #columns = #columns + #columnName
FETCH NEXT FROM tableColumns INTO #columnName
IF ##FETCH_STATUS = 0 SET #columns = #columns + ', '
END
CLOSE tableColumns
DEALLOCATE tableColumns
--**** GENERATE FOREIGN ROW UPDATE SQL ****
DECLARE foreignkeys SCROLL CURSOR
FOR
SELECT con.name,
'[' + SCHEMA_NAME(f.schema_id) + '].[' + f.name + ']' fTable, fc.column_name ,
'[' + SCHEMA_NAME(p.schema_id) + '].[' + p.name + ']' pTable, pc.column_name
FROM sys.foreign_keys con
INNER JOIN sysforeignkeys syscon
ON con.object_id = syscon.constid
INNER JOIN sys.objects f
ON con.parent_object_id = f.object_id
INNER JOIN information_schema.columns fc
ON fc.table_schema = SCHEMA_NAME(f.schema_id)
AND fc.table_name = f.name
AND fc.ordinal_position = syscon.fkey
INNER JOIN sys.objects p
ON con.referenced_object_id = p.object_id
INNER JOIN information_schema.columns pc
ON pc.table_schema = SCHEMA_NAME(p.schema_id)
AND pc.table_name = p.name
AND pc.ordinal_position = syscon.rkey
WHERE '[' + SCHEMA_NAME(p.schema_id) + '].[' + p.name + ']' = #Table
OPEN foreignkeys
DECLARE #FKeyName VARCHAR(100)
DECLARE #FTable VARCHAR(100)
DECLARE #FColumn VARCHAR(100)
DECLARE #PTable VARCHAR(100)
DECLARE #PColumn VARCHAR(100)
--**** RE-WRITE ALL IDS IN THE TABLE ****
SET #sql='DECLARE tablerows CURSOR FOR
SELECT CAST('+#IdColumn+' AS VARCHAR) FROM '+#Table+' ORDER BY '+#IdColumn
PRINT(#sql)
exec(#sql)
OPEN tablerows
DECLARE #rowid VARCHAR(100)
DECLARE #id VARCHAR(100)
FETCH NEXT FROM tablerows INTO #rowid
WHILE ##FETCH_STATUS = 0
BEGIN
--generate new id
INSERT INTO #IdTable VALUES ('')
SELECT #id = CAST(##IDENTITY AS VARCHAR)
IF #rowId <> #Id
BEGIN
PRINT('Modifying '+#Table+': changing '+#rowId+' to '+#id)
SET #sql='SET IDENTITY_INSERT ' + #Table + ' ON
INSERT INTO '+#Table+' ('+#IdColumn+','+#columns+') SELECT '+#id+','+#columns+' FROM '+#Table+' WHERE '+#IdColumn+'='+#rowId
--Updating all foreign rows...
FETCH FIRST FROM foreignkeys
INTO #FKeyName, #FTable, #FColumn, #PTable, #PColumn
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = #sql + '
UPDATE '+#FTable+' SET '+#FColumn+'='+#id+' WHERE '+#FColumn+' ='+#rowId
FETCH NEXT FROM foreignkeys
INTO #FKeyName, #FTable, #FColumn, #PTable, #PColumn
END
SET #sql=#sql + '
DELETE FROM '+#Table+' WHERE '+#IdColumn+'='+#rowId
PRINT(#sql)
exec(#sql)
END
FETCH NEXT FROM tablerows INTO #rowid
END
CLOSE tablerows
DEALLOCATE tablerows
CLOSE foreignkeys
DEALLOCATE foreignkeys
--Revert to normal identity operation - update the identity to the latest id...
DBCC CHECKIDENT(#Table, RESEED, ##IDENTITY)
SET #sql='SET IDENTITY_INSERT ' + #Table + ' OFF'
PRINT(#sql)
exec(#sql)
FETCH NEXT FROM tables
INTO #Table, #IdColumn
END
CLOSE tables
DEALLOCATE tables
DROP TABLE #IdTable
--COMMIT
--ROLLBACK
Why don't you use negative numbers for your standard configuration values and continue to use positive numbers for other things?