Run script in SQLMode changing variables - sql-server

I have this code in a script file:
sqlcmd -E -I -S MyServerName -d MyDb1 -i D:\Updates\script.sql -o D:\Result\MyDb1.txt
sqlcmd -E -I -S MyServerName -d MyDb2 -i D:\Updates\script.sql -o D:\Result\MyDb2.txt
sqlcmd -E -I -S MyServerName -d MyDb3 -i D:\Updates\script.sql -o D:\Result\MyDb3.txt
I have more dbs to update but this is just an example, I have a .bat file that reads each sentence and execute the script.sql into each db, this works fine but now I'm generating scripts using SQL Data tools so visual studio generates the script for me, the problem is that it creates variables and put in a variable the db name, I need to be able to update that variable each time it goes to each db and run the script in sql mode.
This is an example of the script.sql for MyDb1:
/*
Deployment script for MyDb1
This code was generated by a tool.
Changes to this file may cause incorrect behavior and will be lost if
the code is regenerated.
*/
GO
SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON;
SET NUMERIC_ROUNDABORT OFF;
GO
:setvar DictionaryData "DictionaryData"
:setvar DatabaseName "MyDb1"
:setvar DefaultFilePrefix "MyDb1"
GO
:on error exit
GO
/*
Detect SQLCMD mode and disable script execution if SQLCMD mode is not supported.
To re-enable the script after enabling SQLCMD mode, execute the following:
SET NOEXEC OFF;
*/
:setvar __IsSqlCmdEnabled "True"
GO
IF N'$(__IsSqlCmdEnabled)' NOT LIKE N'True'
BEGIN
PRINT N'SQLCMD mode must be enabled to successfully execute this script.';
SET NOEXEC ON;
END
GO
USE [$(DatabaseName)];
GO
PRINT N'Altering [cimpl].[LanguageView]...';
GO
ALTER VIEW [dbo].[LanguageView]
AS
SELECT l.LanguageLabelId, ll.[Name]
FROM [$(DictionaryData)].[dict].[Language] l
INNER JOIN [$(DictionaryData)].[dict].[LanguageType] lt
ON l.[LanguageTypeId] = lt.[LanguageTypeId]
INNER JOIN [$(DictionaryData)].[dict].[LanguageLabel] ll
ON ll.[LanguageLabelId] = l.[LanguageLabelId]
LEFT JOIN [cimpl].[LanguageConfiguration] lc
ON (lc.[LanguageLabelId] = l.[LanguageLabelId])
AND lc.[LanguageTypeId] = lt.[LanguageTypeId]
AND lc.[Active] = 1
WHERE l.Active = 1
GO
The script is much longer, but as an example, you can see the code is generated automatically but for MyDb1, now I need to run it on MyDb2 and MyDb3
How can I replace dynamically the sentence:
:setvar DatabaseName "MyDb1"
by:
:setvar DatabaseName "MyDb2" and :setvar DatabaseName "MyDb3"
at the time is running the sqlcmd?

Related

Trying to spin up a database from the command line if it doesn't exist

I'm trying to feed in a variable name from the command line and create a database if it does not exist. My command line is below:
sqlcmd -S localhost -i 00_SpinUp.sql -v DBName = TEST -o Script00.txt -b
where 00_SpinUp.sql is as follows:
DECLARE #DBNAME VARCHAR(MAX);
SET NOCOUNT ON
GO
IF DB_ID('$(DBNAME)') IS NULL
BEGIN
CREATE DATABASE #DBNAME
END
Yet I'm getting a syntax error. What have I done wrong?
With SQLCMD variables, reference the variable in the script with $(VariableName) rather than as T-SQL variables. No declaration is needed in the script.
SET NOCOUNT ON;
GO
IF DB_ID('$(DBNAME)') IS NULL
BEGIN
CREATE DATABASE [$(DBNAME)];
END;

Why is this batch file not executing automatically?

Basically I have a batch file which calls a SQL Server 2008 R2 stored procedure that interogates that DB for fragmentated tables over 5% fragmented and outputs the report to a text file.
The batch file is set as a Task Scheduler job to run overnight.
In Task Scheduler If I right click the task and select run immediately it executes fine, the script is in turn run on SQL Server and the output is redirected to a text file, everything is working as desired at this point, the problem is when I automate it to trigger overnight.
set local
REM Preparing Timestamp Information
set year=%date:~6,4%
set month=%date:~3,2%
set day=%date:~0,2%
set hour=%time:~0,2%
REM Replace leading space with zero
if “%hour:~0,1%” ==” ” set hour=0%hour:~1,1%
set minute=%time:~3,2%
set seconds=%time:~6,2%
set FILENAMEANDPATH= c:\DBMaintenanceLogs\SP_InspectorLog_%day%-%month%-%year%_%hour%-%minute%-%seconds%.log
sqlcmd -S .\SQLEXPRESS -E -Q "EXEC sp_DBIndexFragmentationInspector #IndexFragmentationPercentage=5" -d MyDBName -o %FILENAMEANDPATH%
The problem is that when left alone the Task Scheduler job will not run and the task says completed successfully with return code 255?
The Task is set to run using the system administrator account.
P.S. The Stored Procedure is:
USE [MyDBName]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_DBIndexFragmentationInspector]
#IndexFragmentationPercentage INT
AS
BEGIN
SELECT
OBJECT_NAME(A.[object_id]) as 'TableName',
B.[name] as 'IndexName',
A.[index_type_desc],
A.[avg_fragmentation_in_percent]
FROM
sys.dm_db_index_physical_stats(db_id(),NULL,NULL,NULL,'LIMITED') A
INNER JOIN
sys.indexes B ON A.[object_id] = B.[object_id] and A.index_id = B.index_id
where [avg_fragmentation_in_percent] > #IndexFragmentationPercentage
order by TableName
END
I've been confronted with exactly the same error. For me, the solution was to add exit 0 at the end of the script. I don't understand why but it worked.

Running .sql file within a SQL query

I have a SQL script I need to run on about 20 different databases.
I basically just need to be able to run some SQL, then have it load and run a file from the disk, do more SQL, run that same script again, etc.
I was hoping to make a SQL script that would basically look something like this:
use database1
go
exec c:\release.sql
go
use database2
go
exec c:\release.sql
go
use database3
go
exec c:\release.sql
go
--etc....
I've looked online a bunch and found a way to do something similar in a batch file with sqlcmd but it isn't working and I don't see how to switch databases that way, either.
Thanks a ton!
Ben
You can switch management studio to sqlcmd mode (query menu) and then run a script with :r script.sql
To do this on a dynamically generated list of databases you have to do some sqlcmd trickery:
set output to file
generate the command to execute
set output to stdout
execute the file
delete the temp file
I assume in this example that the file script.sql exists in c:\temp. Note that the GO statements are important in the script or the sqlcmd parser will get confused.
:OUT $(TEMP)\db.sql
declare #script nvarchar(max)
select #script = isnull(#script, '')
+ 'use ' + name + char(13) + char(10)
+ ':r c:\temp\script.sql' + char(13) + char(10)
from sys.databases
where name like N'%[_]db'
print #script
GO
:OUT stdout
:r $(TEMP)\db.sql
GO
!!del $(TEMP)\db.sql /s /q
You don't need to do this in SSMS. You just need to create a CMD script.
IF you have a static set of databases to run on, then use the following:
#ECHO OFF
SET MyServer="(local)"
SET MyScript="c:\release.sql"
SQLCMD -S %MyServer% -E -i %MyScript% -d database1
SQLCMD -S %MyServer% -E -i %MyScript% -d database2
...
SQLCMD -S %MyServer% -E -i %MyScript% -d database20
IF you have a dynamic set of databases that can be queried for, then use the following:
#ECHO OFF
SET MyServer="(local)"
SET MyScript="c:\release.sql"
SET MyQuery="SET NOCOUNT ON; SELECT [Name] FROM [sys].[databases] sd WHERE sd.[name] LIKE N'%%[_]db' ORDER BY sd.[name];"
FOR /F %%B IN ('SQLCMD -h -1 -S %MyServer% -E -Q %MyQuery%') DO (
REM remove the "echo" from the next line to run the scripts
echo SQLCMD -S %MyServer% -E -i %MyScript% -d %%B -o results-%%B.txt
)
Using the %%B in the output filename will give you a different output file per database, as in:
results-database1_db.txt
results-database2_db.txt
...
Other notes:
Use (local) instead of localhost when connecting to the local, default instance as it uses shared memory while localhost forces a TCP connection.
If you are searching for an underscore in a LIKE statement, enclose it in square brackets else it is a single-character wild card (which still technically works sometimes, but could also match other characters): [_]
Thanks everyone who pitched in! The following seems like it might work (based on #srutzky's answer)
sqlcmd -S "localhost" -E -i "c:\release.sql" -d database1 -o results.txt
The thing I am missing by using a cmd prompt instead of SSMS is that I don't think I can write cursor to loop through each database that ends with "_db" and then execute against that... Here's the SQL I have but I just need to be able to put the link to the SQL file to execute.
link
If I put the release script SQL into this file into the #text variable it doesn't work because it blows up on each GO statement I have in my release.sql file.
declare #text as nvarchar(max)
set #text = N'
-- GET AND RUN SCRIPT FROM DISK!
'
declare C_CURSOR CURSOR FOR
select [Name] from sys.databases
where name like '%_db'
order by name
declare #runtext as nvarchar(max)
declare #DB_Name as nvarchar(200)
OPEN C_CURSOR
fetch next from C_CURSOR INTO #DB_Name
WHILE(##FETCH_STATUS = 0)
BEGIN
print #DB_Name
set #runtext = 'select ''' + #DB_Name + ''' as DatabaseName
use ' + #DB_Name + N'
' + #text
exec sp_executesql #runtext
fetch next from C_CURSOR INTO #DB_Name
END
CLOSE C_CURSOR
DEALLOCATE C_CURSOR
Thanks again!
I ended up combining 2 things. I made a SQL script that creates a cursor to find the databases and then prints a list of commands for a CMD prompt. I then run that in the command prompt. Below is what we output with our sql script and then save as a .bat file that we run. It's working great!
That script is essentially created with the following SQL script:
/*** GET DATABASES IN THE CURSOR QUERY BELOW! */
declare C_CURSOR CURSOR FOR
select [Name] from sys.databases
where name like '%_db'
order by name
/* THIS IS WHERE THE CURSOR STARTS*/
declare #DB_Name as nvarchar(200)
OPEN C_CURSOR
fetch next from C_CURSOR INTO #DB_Name
WHILE(##FETCH_STATUS = 0)
BEGIN
print 'SQLCMD -S "localhost" -E -i "C:\release.sql" -d ' + #DB_Name + ' -o ' + #DB_Name + '_results.txt'
fetch next from C_CURSOR INTO #DB_Name
END
CLOSE C_CURSOR
DEALLOCATE C_CURSOR
That outputs the following which we then run in a .bat file
SQLCMD -S "localhost" -E -i "C:\release.sql" -d database1 -o database1_results.txt
SQLCMD -S "localhost" -E -i "C:\release.sql" -d database2 -o database2_results.txt
SQLCMD -S "localhost" -E -i "C:\release.sql" -d database3 -o database3_results.txt
Thanks everyone!

Error While using SQLCMD in PDW

I'm using SQLCMD in PDW for extracting data into a flat file. The command line syntax is given below:
sqlcmd -S "10.20.30.40,19001" -d MyPDW_DB -U PDW_User -P Password1 -Q "SET QUOTED_IDENTIFIER ON; SELECT * FROM MyPDW_DB.dbo.SampleFact" -o "FactOut.txt" -s"|"
When I try to execute the batch file, I get the following error:
Msg 104409, Level 16, State 1, Server PdwTdsServer, Line 1
Setting QuotedIdentifier to 'OFF' is not supported.
I am assuming this is due to the fact that there is a "comma" in the server name (IP address,Port Number). I can use this command for extracting data from SQL tables. Any idea on how I can make this working for PDW?
Thanks in advance
I got this working partially.
sqlcmd -S "10.20.30.40,19001" -d MyPDW_DB -U PDW_User -P Password1 -I -Q "SELECT * FROM MyPDW_DB.dbo.SampleFact" -o "FactOut.txt" -s"|"
For setting the quoted_identifier OFF, the option to use is "-I". However, I'm still trying to find an alternative for "SET NOCOUNT ON" option which is not supported in PDW. If someone can help me with that, I'd greatly appreciate that.

Setting up a database, schemas, tables and stored procedures all in one click

I have all the scripts to do:
Set up a database.
Create schema/s.
Create tables.
Create stored procedures.
I would like to write a batch file that will have SQL Server run those scripts and consequently my database will be created easier and quicker. For the sake of this example, lets assume that I have a folder with the address C:\folder and inside this folder I have files SetDatabase.sql, SetSchema.sql, SetTable.sql, and SetSP.sql. How would I set all that up on localhost\TSQL2012?
You can do this in powershell using sqlcmd
sqlcmd -S serverName\instanceName -i scripts.sql
The above statement will execute a script.
You can use the :r command in another file (scripts.sql) to store all your scripts.
:r C:\..\script1.sql
:r C:\..\script2.sql
....
set _connectionCredentialsMaster=-S MyServer\MyInstance -d Master -U sa -P mypassword
set _connectionCredentialsMyDatabase=-S MyServer\MyInstance -d MyDatabase -U sa -P mypassword
set _sqlcmd="%ProgramFiles%\Microsoft SQL Server\110\Tools\Binn\SQLCMD.EXE"
%_sqlcmd% -i MyFileCreateDatabase001.sql -b -o MyFileCreateDatabase001.Sql.log %_connectionCredentialsMaster%
%_sqlcmd% -i MyFile001.sql -b -o MyFile001.Sql.log %_connectionCredentialsMyDatabase%
%_sqlcmd% -i MyFile002.sql -b -o MyFile002.Sql.log %_connectionCredentialsMyDatabase%
set _connectionCredentialsMaster=
set _connectionCredentialsMyDatabase=
set _sqlcmd=
Just remember, when you run the 'Create Database' statement, you are actually USING the "Master" database. Then, after MyDatabase is created, you can use it. Thus why the first line in the example above...connects to Master.
The above will let you set the credentials "at the top" "one time"....and keep your lines in the file for each file.
Use SQL data tools to implement your needs. You should study about that before you do.
http://msdn.microsoft.com/en-in/data/tools.aspx

Resources