How to get sql statement in trigger - sql

I have insert, update, delete triggers for every tables to logging actions.
I am retrieving before and after datas from deleted, inserted and wrapping these into xml.
But some logs can't show before and update values.
My sql statement is:
USE [cop]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[Delete] ON [dbo].[Seanslar]
AFTER DELETE
AS
BEGIN
SET NOCOUNT ON
DECLARE #deleted AS XML
SET #deleted = (select * from deleted for xml AUTO, ELEMENTS XSINIL)
DECLARE #logIslem TINYINT
SET #logIslem = 3
DECLARE #tableName VARCHAR(200)
SELECT #tableName = OBJECT_SCHEMA_NAME( parent_id ) + '.' + OBJECT_NAME( parent_id )
FROM sys.triggers
WHERE object_id = ##PROCID
DECLARE #xmlToChar NVARCHAR(MAX)
SET #xmlToChar = CAST(#deleted AS nvarchar(MAX))
IF LEN(#xmlToChar)<10
BEGIN
IF EXISTS(select * from deleted)
select #xmlToChar = CAST(seans_id AS NVARCHAR(MAX)) from deleted
ELSE
SET #xmlToChar = 'Deleted is empty!'
END
DECLARE #allXml AS XML
SET #allXml = '<'+#tableName+'>'+ #xmlToChar +'</'+#tableName+'>'
INSERT INTO [dbo].[Logla]
([logIslem], [trgKullanici_id], [tabloAdi], [logXml])
VALUES
(#logIslem, SUSER_NAME(), #tableName, #allXml)
END
Is there any way to learn "sql statement" executed inside trigger?

There is no practical way to capture the executing SQL statement text inside of a DML Trigger fired by that statement.
You can do this with a DDL (metadata) Trigger, but not a DML (normal) Trigger.
And yes, there are one or two very impractical ways to do it, but I really do not recommend them unless:
You are very, very SQL proficient, and
You really, really need to get it, and
You can afford a lot of development and testing time

Related

How to use nested If statements in SQL trigger

I'm trying to learn SQL triggers to automatically handle events in my database but I'm having some problems with execution.
If I run the following code:
declare #userid numeric(18,0);
declare #username nvarchar(max);
set #userid = 400
execute GetUserNameFromID #userid,#username output
select #username
which calls the following stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE GetUserNameFromID
-- Add the parameters for the stored procedure here
#UserID numeric(18,0),
#UserName nvarchar(MAX) OUT
AS
BEGIN
SET NOCOUNT ON;
SELECT #UserName = u.name from Users u where ID=#UserID
END
GO
I get a nice result 'sometestuser'
But when calling it from my trigger it fails to return a value from the stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER Trigger [dbo].[CheckIfUserHasNoItemsLeft] on [dbo].[Items] for update
As
Begin
set nocount on
declare #inactive_user nvarchar(50);
declare #userid numeric(18,0);
declare #username nvarchar(MAX);
if ( select Count(*) from inserted ) > 1 RaIsError( 'CheckIfIserHasNoItemsLeft: No more than one row may be processed.', 25, 42 ) with log
if update(InactiveUser)
set #inactive_user = (select InactiveUser from inserted)
if #inactive_user is not null
set #userid = (select CID from inserted)
execute GetuserNameFromID #userid,#username output
if #username is not null
insert into tasks (Task) values ('The last item for ' + #username + ' has been marked inactive, check if this user should now be also marked inactive.')
End
InactiveUser is the name of the app user who has marked this item inactive, it is what I am using as a check to see if the item has been set inactive rather than create an additional boolean column just for this purpose.
I'm sure it's something simple but information on If...Then statements for SQL seems to be limited and a lot of answers suggest using Case but the query editor gives me errors about incorrect syntax no matter which way I try to do it that way.
As I'm learning I'm more than happy for someone to show me a completely new way of handling this if what I've done is wrong or bad design. I'm hoping to create a set of triggers that will add items to the tasks table for administrators to check when user accounts appear to be stale and other maintenance checks etc.
I am using SQL server 2005.
Thank you.
Edit: Changed 'value <> null' to 'value is not null'
Edit2: Added HABO's suggestion to throw an error if more than one row is detected.
How about we take a whole new approach to this. Processes like this are exactly why the inline table valued functions were created.
Let's start by converting your stored procedure to an inline table valued function.
CREATE FUNCTION GetUserNameFromID
(
#UserID numeric(18,0)
) RETURNS TABLE
AS RETURN
SELECT u.name
from Users u
where ID = #UserID
GO
That is a LOT simpler and cleaner than that stored procedure with an output variable.
Here is where it really starts to make a difference. Here is what you could do with that trigger using the newly created iTVF.
ALTER Trigger [dbo].[CheckIfUserHasNoItemsLeft] on [dbo].[Items] for update
As Begin
set nocount on
if update(InactiveUser)
insert into tasks (Task)
select 'The last item for ' + u.name + ' has been marked inactive, check if this user should now be also marked inactive.'
from inserted i
cross apply dbo.GetUserNameFromID(i.CID) u
end
This is super simple AND it is fully set based so if you update 1 or 1,000 rows it will work correctly.

How do you check if IDENTITY_INSERT is set to ON or OFF in SQL Server?

I've searched for this, but threads in which it appeared tended to have answers from people who didn't understand the question.
Take the following syntax:
SET IDENTITY_INSERT Table1 ON
How do you do something more like this:
GET IDENTITY_INSERT Table1
I don't want to do anything whatsoever to the data in the database or to the settings to get this information though. Thanks!
Since SET IDENTITY_INSERT is a session sensitive, it is managed in buffer level without storing somewhere. This means we do not need to check the IDENTITY_INSERT status as we never use this key word in current session.
Sorry, no help for this.
Great question though :)
Source: Here
Update
There are ways maybe to do this, also seen in the site I linked, IMO, it is too much effort to be useful.
if
(select max(id) from MyTable) < (select max(id) from inserted)
--Then you may be inserting a record normally
BEGIN
set #I = 1 --SQL wants something to happen in the "IF" side of an IF/ELSE
END
ELSE --You definitely have IDENTITY_INSERT on. Done as ELSE instead of the other way around so that if there is no inserted table, it will run anyway
BEGIN
.... Code that shouldn't run with IDENTITY_INSERT on
END
In summary:
Nathan's solution is the fastest:
SELECT OBJECTPROPERTY(OBJECT_ID('MyTable'), 'TableHasIdentity');
when using an API wrapper, one can reduce the entire check to just checking for rows. For instance when using C#'s SqlDataReaders property HasRows and a query construct like:
SELECT CASE OBJECTPROPERTY(OBJECT_ID('MyTable'), 'TableHasIdentity')
WHEN 1 THEN '1' ELSE NULL END
Ricardo's solution allows more flexibility but requires the Column's identity name
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('MyTable', 'U')
AND name = 'MyTableIdentityColumnName';
Bogdan Bodanov solution, using try/catch would work as well, but additional checking should confine exception handling to cases of IDENTITY_INSERT is already ON for table 'MyTable'. Cannot perform SET operation for table 'MyTable';
You can discover whether or not identity_insert is on, and if so for what table using the code below.
declare #tableWithIdentity varchar(max) = '';
SET IDENTITY_INSERT ExampleTable ON
begin try
create table #identityCheck (id int identity(1,1))
SET IDENTITY_INSERT #identityCheck ON
drop table #identityCheck
end try
begin catch
declare #msg varchar(max) = error_message()
set #tableWithIdentity= #msg;
set #tableWithIdentity =
SUBSTRING(#tableWithIdentity,charindex('''',#tableWithIdentity,1)+1, 10000)
set #tableWithIdentity = SUBSTRING(#tableWithIdentity,1, charindex('''',#tableWithIdentity,1)-1)
print #msg;
drop table #identityCheck
end catch
if #tableWithIdentity<>''
begin
print ('Name of table with Identity_Insert set to ON: ' + #tableWithIdentity)
end
else
begin
print 'No table currently has Identity Insert Set to ON'
end
If you're attempting to turn off IDENTITY_INSERT for some other table to avoid getting an error when you want to set IDENTITY_INSERT on, the following may also work for you. As other have said on this thread IDENTITY_INSERT is a session setting with no direct visibility. However I made the interesting discovery that SET IDENTITY_INSERT OFF doesn't error out for any table that has an identity whether or not IDENTITY_INSERT is ON for that table. So it occurred to me that I could just call SET IDENTITY_INSERT ... OFF for every table with an identity in the database. It feels a bit like a brute force solution, but
I found that the following dynamic SQL block did the trick very nicely.
---- make sure IDENTITY_INSERT is OFF ----
DECLARE #cmd NVARCHAR(MAX)
SET #cmd = CAST((SELECT 'SET IDENTITY_INSERT ' +
QUOTENAME(OBJECT_SCHEMA_NAME(t.object_id)) + '.' +
QUOTENAME(t.name) + ' OFF' + CHAR(10)
FROM sys.columns c
JOIN sys.tables t ON t.object_id = c.object_id
WHERE c.is_identity = 1
ORDER BY 1 FOR XML PATH('')) AS NVARCHAR(MAX))
EXEC sp_executesql #cmd
Very good question. I Have same issue. May be you can try to reset IDENTITY_INSERT using TRY/CATCH? For example, you make the job but not sure if the job is finished and IDENTITY_INSERT is set to OFF.
Why you don't try:
BEGIN TRY
...
END TRY
BEGIN CATCH
SET IDENTITY_INSERT table OFF;
END CATCH;
Also I am not sure that this is working correctly but I see that adding only SET IDENTITY_INSERT ... OFF did not return error. So you can set just in case in the end SET IDENTITY_INSERT ... OFF.
If you want to know about the session variable... Good question, but I cant see where this information would be usefull. In normal execution to check a normal table response to an insert, this should work!
-- If you only want to know if there is identity insert on a given table:
select is_identity
from sys.columns
where object_id = OBJECT_ID('MyTable', 'U') and name = 'column_Name'
-- Or... Use this if you want to execute something depending on the result:
if exists (select *
from sys.columns
where object_id = OBJECT_ID('MyTable', 'U') and is_identity = 1)
... your code considering identity insert
else
... code that should not run with identity insert
Have fun!
Here is my solution. It is very similar to #jmoreno's answer.
You would call it like this
DECLARE #IdentityInsert VARCHAR(20)
EXEC dbo.GetIdentityInsert 'YourDb', 'YourSchema', 'YourTable', #IdentityInsert OUT
SELECT #IdentityInsert
This returns a 1-row recordset with column name IDENTITY_INSERT, that can be either ON, OFF, or NO_IDENTITY (if the given table doesn't have an identity column). It also sets the output parameter #IdentityInsert. So you can adjust the code to whichever method you prefer.
It would be nice to get this into a user-defined function, but unfortunately I couldn't find a way to avoid the TRY..CATCH block, which you cannot use in user-defined functions.
-- ================================================================================
-- Check whether the table specified has its IDENTITY_INSERT set to ON or OFF.
-- If the table does not have an identity column, NO_IDENTITY is returned.
-- Tested on SQL 2008.
-- ================================================================================
CREATE PROCEDURE dbo.GetIdentityInsert
#dbname sysname
, #schemaname sysname
, #table sysname
, #IdentityInsert VARCHAR(20) OUTPUT
AS
BEGIN
SET NOCOUNT ON
DECLARE #OtherTable nvarchar(max)
DECLARE #DbSchemaTable nvarchar(max)
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
DECLARE #ErrorNumber INT;
DECLARE #object_id INT;
SET #DbSchemaTable = #dbname + '.' + #schemaname + '.' + #table
SET #object_id = OBJECT_ID(#DbSchemaTable)
IF #object_id IS NULL
BEGIN
RAISERROR('table %s doesn''t exist', 16, 1, #DbSchemaTable)
RETURN
END
BEGIN TRY
SET #object_id = OBJECT_ID(#DbSchemaTable)
IF OBJECTPROPERTY(#object_id,'TableHasIdentity') = 0
BEGIN
SET #IdentityInsert = 'NO_IDENTITY'
END
ELSE
BEGIN
-- Attempt to set IDENTITY_INSERT on a temp table. This will fail if any other table
-- has IDENTITY_INSERT set to ON, and we'll process that in the CATCH
CREATE TABLE #GetIdentityInsert(ID INT IDENTITY)
SET IDENTITY_INSERT #GetIdentityInsert ON
SET IDENTITY_INSERT #GetIdentityInsert OFF
DROP TABLE #GetIdentityInsert
-- It didn't fail, so IDENTITY_INSERT on #table must set to OFF
SET #IdentityInsert = 'OFF'
END
END TRY
BEGIN CATCH
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE(),
#ErrorNumber = ERROR_NUMBER();
IF #ErrorNumber = 8107 --IDENTITY_INSERT is already set on a table
BEGIN
SET #OtherTable = SUBSTRING(#ErrorMessage, CHARINDEX(char(39), #ErrorMessage)+1, 2000)
SET #OtherTable = SUBSTRING(#OtherTable, 1, CHARINDEX(char(39), #OtherTable)-1)
IF #OtherTable = #DbSchemaTable
BEGIN
-- If the table name is the same, then IDENTITY_INSERT on #table must be ON
SET #IdentityInsert = 'ON'
END
ELSE
BEGIN
-- If the table name is different, then IDENTITY_INSERT on #table must be OFF
SET #IdentityInsert = 'OFF'
END
END
ELSE
BEGIN
RAISERROR (#ErrorNumber, #ErrorMessage, #ErrorSeverity, #ErrorState);
--THROW Use this if SQL 2012 or higher
END
END CATCH
SELECT [IDENTITY_INSERT] = #IdentityInsert
END
GO
you can also use the ObjectProperty method to determine if a table has an identity:
DECLARE #MyTableName nvarchar(200)
SET #MyTableName = 'TestTable'
SELECT CASE OBJECTPROPERTY(OBJECT_ID(#MyTableName), 'TableHasIdentity')
WHEN 1 THEN 'has identity'
ELSE 'no identity columns'
END as HasIdentity

SQL Server Create Login using inserted values from Trigger

I have a table of users in sql serevr 2012 and wish to create corresponding database login users using inserted values from an insert trigger on the users table.
Everything seems to work ok except that I cannot properly use a referenced value in the inserted table
though I have tested the value to make sure it exists
is it possible that the create login procedure cannot accept variables
ALTER TRIGGER [dbo].[addsyslogin]
ON [dbo].[Users]
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
SET QUOTED_IDENTIFIER OFF;
Declare #u varchar(10);
SELECT #u=i.userid from inserted i;
print #u;
CREATE LOGIN [#u] WITH PASSWORD='water123GH', DEFAULT_DATABASE=[waterdb], DEFAULT_LANGUAGE=[British], CHECK_EXPIRATION=OFF, CHECK_POLICY=ON
END
I have researched on quoted_identifiers but I still have this problem.
What happens is that a database user is actually created with userid=#u instead of the value in #u.
Thanks for your notes and feedback
try this:
SET #SQL = 'CREATE LOGIN ' + #u+ ' WITH PASSWORD = ''water123GH'', DEFAULT_DATABASE=[waterdb], DEFAULT_LANGUAGE=[British], CHECK_EXPIRATION=OFF, CHECK_POLICY=ON';
EXECUTE(#SQL);
This is a curious thing to do, but you need to...
allow for multiple rows
escalate privileges
use dynamic SQL
Other notes:
I'd suggest that you need a separate stored procedure in master called by your trigger to deal with the escalation
If you don't require escalation, then your privileges are too high
Rough code:
ALTER TRIGGER [dbo].[addsyslogin] ON [dbo].[Users] FOR INSERT
WITH EXECUTE AS --soemthing
AS
SET NOCOUNT ON;
Declare #u varchar(10), #uid int;
SELECT TOP 1 #uid = i.userid, #u = i.userid
FROM inserted i
ORDER BY i.userid;
WHILE #u IS NOT NULL
BEGIN
SET #SQL = 'CREATE LOGIN ' + #u+ ' WITH PASSWORD = ''water123GH'', DEFAULT_DATABASE=[waterdb], DEFAULT_LANGUAGE=[British], CHECK_EXPIRATION=OFF, CHECK_POLICY=ON';
EXECUTE(#SQL);
SET #u = NULL;
SELECT TOP 1 #uid = i.userid, #u = i.userid
FROM inserted i
WHERE i.userid > #uid
ORDER BY i.userid;
END
GO

use variable in sql trigger creation

I'm trying to create a trigger dynamically and need to replace part of the create statement, in particular the database name that a join is done on.
CREATE TRIGGER [dbo].[tr_UseType_update] ON [dbo].[UseType] FOR UPDATE AS
BEGIN
SET NOCOUNT ON
INSERT [dbo].[UseType]
SELECT 'Updated', i.*
FROM inserted
INNER JOIN [(SELECT DB_NAME())].[dbo].[UseType] i ON inserted.[UseTypeID] = i.[UseTypeID]
END
I've tried this but obviously it just uses the text it doesn't actually evaluate SELECT DB_NAME(), is there a way to get it to evaluate this value.
I'm using sql 2005/8. We have a deployment tool that uses sqlcmd so a fallback will be to change the tool to take in variables that can be passed to sqlcmd but if I can reduce the amount of work and do it in the script itself (which is generated) that would be handy.
The only way would be to use dynamic sql.
There's no other way to parameterize database names or table names.
That being said, dynamically creating a trigger like this seems like a perilous idea.
The easiest way to do what you want will be something like:
USE MyDataBase
DECLARE #DB varchar(100) = 'MyOtherDatabaseThatIAmJoiningToInTheInsert'
DECLARE #SQL Varchar (MAX)
SET #SQL = '
CREATE TRIGGER [dbo].[tr_UseType_update] ON [dbo].[UseType] FOR UPDATE AS
BEGIN
SET NOCOUNT ON
INSERT [dbo].[UseType]
SELECT ''Updated'', i.*
FROM inserted
INNER JOIN ' + #DB + '.[dbo].[UseType] i ON inserted.[UseTypeID] = i.[UseTypeID]
END'
It's mandatory to read this before doing any dynamic sql code, though!
You could potentially execute dynamic sql and load that into a temp table which in turn would be available to the trigger.
CREATE TRIGGER [dbo].[tr_UseType_update] ON [dbo].[UseType]
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON
CREATE TABLE #t (userTypeId INT)
DECLARE #sql NVARCHAR(500) SET #sql = 'insert into #t select userTypeID from ' + DB_NAME() + '.dbo.UseType'
EXEC sp_executeSQL #sql
INSERT [dbo].[UseType]
SELECT 'Updated',
i.*
FROM inserted
INNER JOIN #t i ON inserted.[UseTypeID] = i.[UseTypeID]
END

Is there a way to persist a variable across a go?

Is there a way to persist a variable across a go?
Declare #bob as varchar(50);
Set #bob = 'SweetDB';
GO
USE #bob --- see note below
GO
INSERT INTO #bob.[dbo].[ProjectVersion] ([DB_Name], [Script]) VALUES (#bob,'1.2')
See this SO question for the 'USE #bob' line.
Use a temporary table:
CREATE TABLE #variables
(
VarName VARCHAR(20) PRIMARY KEY,
Value VARCHAR(255)
)
GO
Insert into #variables Select 'Bob', 'SweetDB'
GO
Select Value From #variables Where VarName = 'Bob'
GO
DROP TABLE #variables
go
The go command is used to split code into separate batches. If that is exactly what you want to do, then you should use it, but it means that the batches are actually separate, and you can't share variables between them.
In your case the solution is simple; you can just remove the go statements, they are not needed in that code.
Side note: You can't use a variable in a use statement, it has to be the name of a database.
I prefer the this answer from this question
Global Variables with GO
Which has the added benefit of being able to do what you originally wanted to do as well.
The caveat is that you need to turn on SQLCMD mode (under Query->SQLCMD) or turn it on by default for all query windows (Tools->Options then Query Results->By Default, open new queries in SQLCMD mode)
Then you can use the following type of code (completely ripped off from that same answer by Oscar E. Fraxedas Tormo)
--Declare the variable
:setvar MYDATABASE master
--Use the variable
USE $(MYDATABASE);
SELECT * FROM [dbo].[refresh_indexes]
GO
--Use again after a GO
SELECT * from $(MYDATABASE).[dbo].[refresh_indexes];
GO
If you are using SQL Server you can setup global variables for entire scripts like:
:setvar sourceDB "lalalallalal"
and use later in script as:
$(sourceDB)
Make sure SQLCMD mode is on in Server Managment Studi, you can do that via top menu Click Query and toggle SQLCMD Mode on.
More on topic can be found here:
MS Documentation
Temp tables are retained over GO statements, so...
SELECT 'value1' as variable1, 'mydatabasename' as DbName INTO #TMP
-- get a variable from the temp table
DECLARE #dbName VARCHAR(10) = (select top 1 #TMP.DbName from #TMP)
EXEC ('USE ' + #dbName)
GO
-- get another variable from the temp table
DECLARE #value1 VARCHAR(10) = (select top 1 #TMP.variable1 from #TMP)
DROP TABLE #TMP
It's not pretty, but it works
Create your own stored procedures which save/load to a temporary table.
MyVariableSave -- Saves variable to temporary table.
MyVariableLoad -- Loads variable from temporary table.
Then you can use this:
print('Test stored procedures for load/save of variables across GO statements:')
declare #MyVariable int = 42
exec dbo.MyVariableSave #Name = 'test', #Value=#MyVariable
print(' - Set #MyVariable = ' + CAST(#MyVariable AS VARCHAR(100)))
print(' - GO statement resets all variables')
GO -- This resets all variables including #MyVariable
declare #MyVariable int
exec dbo.MyVariableLoad 'test', #MyVariable output
print(' - Get #MyVariable = ' + CAST(#MyVariable AS VARCHAR(100)))
Output:
Test stored procedures for load/save of variables across GO statements:
- Set #MyVariable = 42
- GO statement resets all variables
- Get #MyVariable = 42
You can also use these:
exec dbo.MyVariableList -- Lists all variables in the temporary table.
exec dbo.MyVariableDeleteAll -- Deletes all variables in the temporary table.
Output of exec dbo.MyVariableList:
Name Value
test 42
It turns out that being able to list all of the variables in a table is actually quite useful. So even if you do not load a variable later, its great for debugging purposes to see everything in one place.
This uses a temporary table with a ## prefix, so it's just enough to survive a GO statement. It is intended to be used within a single script.
And the stored procedures:
-- Stored procedure to save a variable to a temp table.
CREATE OR ALTER PROCEDURE MyVariableSave
#Name varchar(255),
#Value varchar(MAX)
WITH EXECUTE AS CALLER
AS
BEGIN
SET NOCOUNT ON
IF NOT EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
BEGIN
DROP TABLE IF EXISTS ##VariableLoadSave
CREATE TABLE ##VariableLoadSave
(
Name varchar(255),
Value varchar(MAX)
)
END
UPDATE ##VariableLoadSave SET Value=#Value WHERE Name=#Name
IF ##ROWCOUNT = 0
INSERT INTO ##VariableLoadSave SELECT #Name, #Value
END
GO
-- Stored procedure to load a variable from a temp table.
CREATE OR ALTER PROCEDURE MyVariableLoad
#Name varchar(255),
#Value varchar(MAX) OUT
WITH EXECUTE AS CALLER
AS
BEGIN
IF EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
BEGIN
IF NOT EXISTS(SELECT TOP 1 * FROM ##VariableLoadSave WHERE Name=#Name)
BEGIN
declare #ErrorMessage1 as varchar(200) = 'Error: cannot find saved variable to load: ' + #Name
raiserror(#ErrorMessage1, 20, -1) with log
END
SELECT #Value=CAST(Value AS varchar(MAX)) FROM ##VariableLoadSave
WHERE Name=#Name
END
ELSE
BEGIN
declare #ErrorMessage2 as varchar(200) = 'Error: cannot find saved variable to load: ' + #Name
raiserror(#ErrorMessage2, 20, -1) with log
END
END
GO
-- Stored procedure to list all saved variables.
CREATE OR ALTER PROCEDURE MyVariableList
WITH EXECUTE AS CALLER
AS
BEGIN
IF EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
BEGIN
SELECT * FROM ##VariableLoadSave
ORDER BY Name
END
END
GO
-- Stored procedure to delete all saved variables.
CREATE OR ALTER PROCEDURE MyVariableDeleteAll
WITH EXECUTE AS CALLER
AS
BEGIN
DROP TABLE IF EXISTS ##VariableLoadSave
CREATE TABLE ##VariableLoadSave
(
Name varchar(255),
Value varchar(MAX)
)
END
If you just need a binary yes/no (like if a column exists) then you can use SET NOEXEC ON to disable execution of statements. SET NOEXEC ON works across GO (across batches). But remember to turn EXEC back on with SET NOEXEC OFF at the end of the script.
IF COL_LENGTH('StuffTable', 'EnableGA') IS NOT NULL
SET NOEXEC ON -- script will not do anything when column already exists
ALTER TABLE dbo.StuffTable ADD EnableGA BIT NOT NULL CONSTRAINT DF_StuffTable_EnableGA DEFAULT(0)
ALTER TABLE dbo.StuffTable SET (LOCK_ESCALATION = TABLE)
GO
UPDATE dbo.StuffTable SET EnableGA = 1 WHERE StuffUrl IS NOT NULL
GO
SET NOEXEC OFF
This compiles statements but does not execute them. So you'll still get "compile errors" if you reference schema that doesn't exist. So it works to "turn off" the script 2nd run (what I'm doing), but does not work to turn off parts of the script on 1st run, because you'll still get compile errors if referencing columns or tables that don't exist yet.
You can make use of NOEXEC follow he steps below:
Create table
#temp_procedure_version(procedure_version varchar(5),pointer varchar(20))
insert procedure versions and pointer to the version into a temp table #temp_procedure_version
--example procedure_version pointer
insert into temp_procedure_version values(1.0,'first version')
insert into temp_procedure_version values(2.0,'final version')
then retrieve the procedure version, you can use where condition as in the following statement
Select #ProcedureVersion=ProcedureVersion from #temp_procedure_version where
pointer='first version'
IF (#ProcedureVersion='1.0')
BEGIN
SET NOEXEC OFF --code execution on
END
ELSE
BEGIN
SET NOEXEC ON --code execution off
END
--insert procedure version 1.0 here
Create procedure version 1.0 as.....
SET NOEXEC OFF -- execution is ON
Select #ProcedureVersion=ProcedureVersion from #temp_procedure_version where
pointer='final version'
IF (#ProcedureVersion='2.0')
BEGIN
SET NOEXEC OFF --code execution on
END
ELSE
BEGIN
SET NOEXEC ON --code execution off
END
Create procedure version 2.0 as.....
SET NOEXEC OFF -- execution is ON
--drop the temp table
Drop table #temp_procedure_version