ALLUSERSPROFILE OF the HOST SQL Server? - sql

I need to write a SQL Server 2008R2 compatible script to create a share. The script will be executed from inside VB6 code but I am pretty sure that's a moot point here.
The following is PSEUDOCODE at the end
CREATE PROCEDURE [dbo].[create_Server_share]
#TheShare VARCHAR(50),
#TheDIR VARCHAR(250) = NULL
AS
BEGIN
IF (#TheDIR IS NULL) -- ALLUSERSPROFILE usually C:\Programdata
SET #TheDIR = ENVREFERENCE('%ALLUSERSPROFILE%')+ '\XYZ'
....
I already see that ENVREFERENCE is NOT available in SQL Server 2008 R2 (which is the oldest version I have to accomodate for our clients)
But I am not married to using ENVREFERENCE either - I just want the HOST MACHINE to give me its environment return for ALLUSERSPROFILE (obviously I should not grab this value from the executing code in the application because I will be getting the CLIENT's value instead of the desired HOST server's value; hence my desire to execute it from the T-SQL script)
So do any SQL guru's have some insight into this?
Thanks in advance.
Harry

Can't say this is completely bulletproof, but I past the first few dozen tests.
Thanks to Jeroen Mostert I realized I had my access to %ALLUSERSPROFILES% already on the Host server. the script then became something I could do...
-- create_Server_share.sql written by Harry Abramowski on 6/26/2018
-- we ARE NOT doing these steps in a command line prompt nor in VB6
-- because this share has to be made **ON** THE SERVER!
-- stored procs are a bitch!
go
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF EXISTS(SELECT 1
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_NAME = 'create_Server_share'
AND SPECIFIC_SCHEMA = 'dbo')
BEGIN
DROP PROCEDURE create_Server_share
END
go
CREATE PROCEDURE [dbo].[create_Server_share]
#TheShare varchar(50),
#TheDrive char=null,
#TheDIR varchar(250)=null
AS
BEGIN
if (#TheDIR is null)
set #TheDIR = '%ALLUSERSPROFILE%\XYZ'
if (#TheDrive is null)
set #TheDrive = 'X'
DECLARE #answer as varchar(MAX)
declare #myString as varchar(1000)
DECLARE #i INT
-- JUST in case its not already set up, let's enable use of the reconfig in SQL
EXEC sp_configure 'show advanced options', 1; --might not be needed
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell',1; -- wont hurt to assume it IS needed
RECONFIGURE;
-- net share XYZShare=C:\Programdata\XYZ /grant:everyone,FULL
a_redo:
set #myString = ('net share ' + #TheShare +'=' + #TheDIR + ' /grant:everyone,FULL')
CREATE TABLE #xyzout ([outputtext] varchar(MAX))
Insert into #xyzout (outputtext) EXECUTE xp_cmdshell #myString
-- what about The system cannot find the file specified.???
if exists(select #xyzout.outputtext from #xyzout where #xyzout.outputtext = 'The system cannot find the file specified.')
begin
set #myString = ('mkdir ' + #TheDIR)
EXECUTE xp_cmdshell #mystring
print ('The directory ' + #TheDIR + ' was created')
drop table #xyzout
goto a_redo -- yeah I know!
end
-- was there an error - was it just an "already exists" message? let's see
set #answer = (select top 1 outputtext from #xyzout)
print #answer
-- now update systemProps table so the client machines know there's a share and what drive they should map it to
if charindex('system error',lower(#answer))= 0
IF NOT EXISTS (SELECT a.* FROM syscolumns a, sysobjects b
WHERE a.name = 'XYZShare' AND
a.id = b.id AND
b.name = 'systemProps')
ALTER TABLE system ADD XYZShare NVARCHAR(1000) NULL ;
if charindex('system error',lower(#answer))= 0
begin
update systemProps set XYZShare = (#TheDrive + '=\\' +
CAST(serverproperty('MachineName') as varchar) + '\' + #TheShare );
select systemProps.XYZShare from systemProps;
return 0;
end
else
begin
select * from #xyzout where not(outputtext is null)
return 1;
end
EXEC sp_configure 'xp_cmdshell',0; --let's leave that off?
RECONFIGURE;
DROP TABLE #xyzout
---- if you need to delete the SHARE ITSELF you COULD use this: EXEC XP_CMDSHELL 'net share Xshared /delete'
--HOWEVER you can easily do either from the windows explorer in NETWORK view or My Computer view
END
GRANT EXECUTE ON dbo.create_Server_share TO PUBLIC
GO
Hope this is useful to someone. You guys always come through for me!

Related

How do I resolve this "Access is denied" error which occurs when using xp_cmdshell in Microsoft SQL?

This is my entire routine:
Declare #AttFileType as char(5), #HQCo as int, #FormName as Varchar(15), #KeyID as VarChar(10), #UniqueID as uniqueidentifier, #FilePath as Varchar(100), #StringCommand as Varchar(200)
Declare #AttID as int
DECLARE #cmd as VARCHAR(500)
DECLARE #cmd2 as VARCHAR(500)
CREATE TABLE #tmp(eFileName VARCHAR(100));
INSERT INTO #tmp
EXEC xp_cmdshell 'dir /B C:\Users\*****\Desktop\Test_Images';
Declare #FileName varchar(100)
Set #UniqueID = NewID()
While (Select Count(*) From #tmp where eFileName is not null) > 0
Begin
Select Top 1 #FileName = eFileName From #tmp
Set #FilePath = 'C:\Users\*****\Desktop\Test_Images\' + #FileName
Set #AttID = (Select TOP 1 AttachmentID FROM dbo.bHQAF ORDER BY AttachmentID DESC) + 1
Set #AttFileType = '.jpg'
Insert Into dbo.bHQAF (AttachmentID, AttachmentFileType)
Select #AttID, #AttFileType
SET #cmd = '
Declare #AttID2 as int, #AttFileType2 as char(5), #FilePath2 as Varchar(100)
Set #AttFileType2 = ''.jpg''
Set #AttID2 = (Select TOP 1 AttachmentID FROM dbo.bHQAF ORDER BY AttachmentID DESC)
Update dbo.bHQAF
Set AttachmentData = (SELECT * From OPENROWSET (Bulk ''' + #FilePath + ''', Single_Blob) rs)
Where AttachmentID = #AttID2 and AttachmentFileType = #AttFileType2'
Exec (#cmd)
Set #HQCo = 101
Set #FormName = 'HRCompAssets'
Set #KeyID = 'KeyID=2'
Insert Into dbo.bHQAT (HQCo, AttachmentID, FormName, KeyField, UniqueAttchID)
Select #HQCo, #AttID, #FormName, #KeyID, #UniqueID
Insert Into dbo.bHQAI (AttachmentID, HRCo)
Select #AttID, #HQCo
Update dbo.bHQAT
Set Description = 'TEST3', AddDate = GETDATE(), AddedBy = '****', DocAttchYN = 'N', DocName = 'Database', OrigFileName = #FileName, TableName = 'HRCA'
Where AttachmentID = #AttID and HQCo = #HQCo
Insert Into dbo.bHQAI (AttachmentID, HRCo)
Select #AttID, 101
Update dbo.bHRCA
Set UniqueAttchID = #UniqueID
Where HRCo = 101 and Asset = '00001'
Delete from #tmp Where eFileName = #FileName
End
I have verified that the code works, for loading a single image into the server, without this bit here:
-- Declarations here
CREATE TABLE #tmp(eFileName VARCHAR(100));
INSERT INTO #tmp
EXEC xp_cmdshell 'dir /B C:\Users\*****\Desktop\Test_Images';
While (Select Count(*) From #tmp where eFileName is not null) > 0
Begin
Select Top 1 #FileName = eFileName From #tmp
-- Rest of code here
Delete from #tmp Where eFileName = #FileName
End
But once the while loop and xp_cmdshell statements are added, the file name is returned as "Access is denied".
Any help would be appreciated!
I'm not an expert in SQL, but I've been asked to load about 1000 PDF and JPEG files into the database and a script seemed to be the most logical approach.
When all is said and done, I would like the script to grab each image from the folder and load it into the database.
I'm open to using a different looping method if necessary.
Edit:
I have also tried adding the following to the beginning of the code which didn't resolve the issue:
--Allow for SQL to use cmd shell
EXEC sp_configure 'show advanced options', 1 -- To allow advanced options to be changed.
RECONFIGURE -- To update the currently configured value for advanced options.
EXEC sp_configure 'xp_cmdshell', 1 -- To enable the feature.
RECONFIGURE -- To update the currently configured value for this feature.
I also went into Facets > Surface Area Configuration and made sure xp_cmdshell is enabled / allowed (true). It was also already marked true under Facets > Server Security.
There are a couple of possible issues here.
xp_cmdShell runs on the server. If that machine does not have a folder called C:\Users\*****\Desktop\Test_Images it will not work.
xp_CmdShell runs using the service account. If that account does not have permissions on the target folder it will fail.
xp_CmdShell has to be enabled. From MSDN.
The xp_cmdshell option is a SQL Server server configuration option
that enables system administrators to control whether the xp_cmdshell
extended stored procedure can be executed on a system. By default, the
xp_cmdshell option is disabled on new installations and can be enabled
by using the Policy-Based Management or by running the sp_configure
system stored procedure.
I figured it out!
Thank you #destination-data & #GarethLyons for all the help!
You guys were right, there was an issue with the folder permissions I didn't realize that I would have to go into the folder and manually update the permissions to include "Service". Once I did, everything worked perfectly!
Thank you both again, sorry for the confusion.
For anyone else who is having this issue in the future, do the following first:
1) Go to the folder
2) Right click
3) Select properties
4) Select the Security tab
5) Click Advanced
6) Click Add
7) Click select a principle
8) Enter "Service" and Check Names
9) Select Service and click OK
10) Select the appropriate permissions under "Basic Permissions"
11) Select "Only apply these permissions to the object in this container"
12) Apply the changes and try running xp_cmdshell again

Executing SQL query on multiple databases

I know my post has a very similar title to other ones in this forum, but I really couldn't find the answer I need.
Here is my problem, I have a SQL Server running on my Windows Server. Inside my SQL Server, I have around 30 databases. All of them have the same tables, and the same stored procedures.
Now, here is the problem, I have this huge script that I need to run in all of these databases. I wish I could do it just once against all my databases.
I tried a couple things like go to "view" >> registered servers >> local server groups >> new server registration. But this solution is for many servers, not many databases.
I know I could do it by typing the database name, but the query is really huge, so it would take too long to run in all databases.
Does anybody have any idea if that is possible?
You can use WHILE loop over all database names and inside loop execute query with EXECUTE. I think that statement SET #dbname = ... could be better, but this works too.
DECLARE #rn INT = 1, #dbname varchar(MAX) = '';
WHILE #dbname IS NOT NULL
BEGIN
SET #dbname = (SELECT name FROM (SELECT name, ROW_NUMBER() OVER (ORDER BY name) rn
FROM sys.databases WHERE name NOT IN('master','tempdb')) t WHERE rn = #rn);
IF #dbname <> '' AND #dbname IS NOT NULL
EXECUTE ('use '+QUOTENAME(#dbname)+';
/* Your script code here */
UPDATE some_table SET ... ;
');
SET #rn = #rn + 1;
END;
Consider running the script in SQLCMD Mode from SSMS (Query--SQLCMD Mode). This way, you can save the script to a file and run it in the context of each of the desired databases easily:
USE DB1;
:r C:\SqlScript\YourLargeScript.sql
GO
USE DB2;
:r C:\SqlScript\YourLargeScript.sql
GO
USE DB3;
:r C:\SqlScript\YourLargeScript.sql
GO
This technique can also be used to run the script against databases on other servers with the addition of a :CONNECT command. The connection reverts back to initial server/database after execution of the entire script:
:CONNECT SomeServer
USE DB4;
:r C:\SqlScript\YourLargeScript.sql
GO
:CONNECT SomeOtherServer
USE DB5;
:r C:\SqlScript\YourLargeScript.sql
GO
Important gotcha: Note GO batch separators are needed for :CONNECT to work as expected. I recommend including GO in the the invoking script like the above example but GO as the last line in the :r script file will also provide the desired results. Without GO in this example (or at the end of the script file), the script would run twice on SomeServer and not run against SomeOtherServer at all.
ApexSQL Propagate is the tool which can help in this situation. It is used for executing single or multiple scripts on multiple databases, even multiple servers. What you should do is simply select that script, then select all databases against which you want to execute that script:
When you load scripts and databases you should just click the “Execute” button and wait for the results:
You can write script like this
DECLARE CURSOR_ALLDB_NAMES CURSOR FOR
SELECT name
FROM Sys.Databases
WHERE name NOT IN('master', 'tempdb')
OPEN CURSOR_ALLDB_NAMES
FETCH CURSOR_ALLDB_NAMES INTO #DB_NAME
WHILE ##Fetch_Status = 0
BEGIN
EXEC('UPDATE '+ #DB_NAME + '..SameTableNameAllDb SET Status=1')
FETCH CURSOR_ALLDB_NAMESINTO INTO #DB_NAME
END
CLOSE CURSOR_ALLDB_NAMES
this is the normal way of doing this :
suppose you want to do a select on database DBOther than it would be :
select * from DBOther..TableName
Also check if the table or view is on the dbo schema, if not you should add the schema also : Please notice I use only one dot now after the database name
select * from DBOther.dbo.ViewName
If any of the databases is on another server on another machine, than make sure the Database is in the Linked Server.
Then you can access the table or view on that database via:
SELECT * FROM [AnotherServerName].[DB].[dbo].[Table]
Here is another way that does not requires typing the database name :
use DB1
go
select * from table1
go
use DB2
go
select * from table1
go
Note that this will only work if the tables and fields are exact the same on each database
You can use the following script to run the same script on a set of databases. Just change the filter in the insert line.
declare #dbs table (
dbName varchar(100),
done bit default 0
)
insert #dbs select [name], 0 FROM master.dbo.sysdatabases WHERE [Name] like 'targets_%'
while (exists(select 1 from #dbs where done = 0))
begin
declare #db varchar(100);
select top 1 #db = dbName from #dbs where done = 0;
exec ('
use [' + #db + '];
update table1 set
col1 = '''',
col2 = 1
where id = ''45b6facb-510d-422f-a48c-687449f08821''
');
print #db + ' updated!';
update #dbs set done = 1 where dbName = #db;
end
If your SQL Server version does not support table variables, just use Temp Tables but don`t forget to drop them at the end of the script.
Depending on the requirement, you can do this:
declare #dbName nvarchar(100)
declare #script nvarchar(max)
declare #dbIndex bigint = 0
declare #dbCount bigint = (
select count(*) from
sys.databases
)
declare crs_databases cursor for
(
select
[name]
from
sys.databases
)
open crs_databases
fetch next from crs_databases into #dbName
while ##FETCH_STATUS = 0
begin
set #dbIndex = #dbIndex+1
set #script = concat(#script,
' select Id from ['+#dbName+']..YourTableName ',
case
when #dbIndex = #dbCount then ''
else 'union'
end)
fetch next from crs_databases into #dbName
end
select #script
close crs_databases
deallocate crs_databases
Please note that the double dotted notation assumes that the schema is dbo. Otherwise, you need to explicitly write down the schema.
select Id from ['+#dbName+'].schema.YourTableName
When you need to execute stored procedures on each server, the #script variable will have another content.

Invalid column name: SQL Server 2008 R2

I have a stored procedure I use to extract binary data from SQL tables to create physical files. I have used it a couple of times on tables with the same structure restored from SQL backups, and it works OK.
Today I wanted to extract some binary data from a new table inside a restored backup. I opened the stored procedure I have been using and set about altering the code. Once I was happy with the changes I tried to execute the 'ALTER' statement. Unfortunetly, both of the column names I have used are 'invalid' despite existing on the the 'Document' table.
I have read a number of other threads regarding 'invalid column name' errors, but the majority of these seem to be typing errors. I've checked my column names numurous times (intelli sense even lets me put in 'Document.Document_ID' and 'Document.Document_Filename' but they still fail).
Any ideas where I am going wrong?
Source:
USE [Example Live]
GO
/****** Object: StoredProcedure [dbo].[FileExport] Script Date: 10/18/2012 11:42:14 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Chris Murray
-- Create date: 18/10/2012
-- Description: Exports binary file data
-- =============================================
ALTER PROCEDURE [dbo].[FileExport]
-- Add the parameters for the stored procedure here
#OutputFilePath VARCHAR(500) = 'C:\Conv\Example\In\Afiles\'
AS
BEGIN
DECLARE #totrow int
DECLARE #currow int
DECLARE #result int
DECLARE #nsql nvarchar(4000)
DECLARE #sqlStatements table (ID int IDENTITY(1, 1), SqlStatement varchar(max))
INSERT
INTO #sqlStatements
SELECT 'BCP "SELECT Document_Data FROM [Example Live].[dbo].[Document] WHERE Document_ID = '''
+ CAST(Document_ID AS VARCHAR(500)) + '''" queryout ' + #OutputFilePath
+ CAST(Document_Filename AS VARCHAR(500)) + ' -S localhost\SQLEXPRESS2008 -T -f C:\Conv\Example\In\AFiles\Images.fmt'
FROM dbo.Photograph
SET #totrow = ##ROWCOUNT
SET #currow = 1
WHILE #totrow > 0 and #currow <= #totrow
BEGIN
SELECT #nsql = SqlStatement
FROM #sqlStatements
WHERE ID = #currow
EXEC #result = xp_cmdshell #nsql
SET #currow = #currow + 1
END
END
Thanks for the ideas everyone.
The problem was of my own making, and a rookie mistake. I neglected to point the SELECT statement at the correct table..

How can I verify that a file exists in Windows with SQL?

I have a SQL Server running on my Windows Server and, at a specific column of a table, I have the path for a Zip file (which in turn has the source of the data stored in the database). Some of these are not valid (do not match the data in database). I need to make SQL Server verify that these Zip files exist and that they match the column that stores the path and name of the zip file. This way I will delete the wrong file-path column correspondences.
you can use the undocumented proc xp_fileexist will return 1 if it exists and 0 otherwise
SET NOCOUNT ON
DECLARE #iFileExists INT
EXEC master..xp_fileexist 'c:\bla.txt',
#iFileExists OUTPUT
select #iFileExists
You can use xp_fileexist however please note it is undocumented and unsupported.
You can use SQLCLR, however you didn't bother specifying what version of SQL Server you're using, so it may not be relevant - and in any case it is disabled by default, and security policies prevent its use in some places.
You can use a #temp table and xp_cmdshell, however xp_cmdshell is typically disabled for the same reasons as SQLCLR.
/* if you need to enable xp_cmdshell:
exec master..sp_configure 'show adv', 1;
reconfigure with override;
exec master..sp_configure 'xp_cmdshell', 1;
reconfigure with override;
exec master..sp_configure 'show adv', 0;
reconfigure with override;
*/
SET NOCOUNT ON;
DECLARE
#file VARCHAR(1000),
#path VARCHAR(255),
#cmd VARCHAR(2048);
SELECT
#file = 'foo.zip',
#path = 'C:\wherever\';
SELECT #cmd = 'dir /b "' + #path + #file + '"';
CREATE TABLE #x(a VARCHAR(1255));
INSERT #x EXEC master..xp_cmdshell #cmd;
IF EXISTS (SELECT 1 FROM #x WHERE a = #file)
PRINT 'file exists';
ELSE
PRINT 'file does not exist';
DROP TABLE #x;
EDIT based on new requirements. It shows a list of files either in the table or in the database, and indicates whether the file is in only one location or both. It assumes that path + file is <= 900 characters long (merely to be able to use an index on at least one side).
USE tempdb;
GO
CREATE TABLE dbo.files(f VARCHAR(1000));
INSERT dbo.files(f) SELECT 'zip_that_does_not_exist.zip'
UNION ALL SELECT 'c:\path\file_that_does_not_exist.zip'
UNION ALL SELECT 'c:\path\file_that_exists.zip'
UNION ALL SELECT 'zip_that_exists.zip';
DECLARE
#path VARCHAR(255),
#cmd VARCHAR(2048);
SELECT
#path = path_column,
#cmd = 'dir /b "' + path_column + '"'
FROM
dbo.table_that_holds_path;
CREATE TABLE #x(f VARCHAR(900) UNIQUE);
INSERT #x EXEC master..xp_cmdshell #cmd;
DELETE #x WHERE f IS NULL;
UPDATE #x SET f = LOWER(f);
WITH f AS
(
SELECT f = REPLACE(LOWER(f), LOWER(#path), '')
FROM dbo.files
)
SELECT
[file] = COALESCE(x.f, f.f),
[status] = CASE
WHEN x.f IS NULL THEN 'in database, not in folder'
WHEN f.f IS NULL THEN 'in folder, not in database'
ELSE 'in both' END
FROM
f FULL OUTER JOIN #x AS x
ON x.f = f.f;
DROP TABLE #x, dbo.files;

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