Control stored procedure outputs - sql

I have the following stored procedure:
SET #sql = 'RESTORE DATABASE ' + quotename(#dbname) + '
FROM DISK = N''E:\sql\template_' + #dbnamebak + '.bak''
WITH FILE = 1,
MOVE N''FromTemplate' + #dbname + '.Data'' TO N''E:\sql\' + #dbname + '.mdf'',
MOVE N''FromTemplate' + #dbname + '.Log'' TO N''E:\sql\' + #dbname + '_log.LDF'', NOUNLOAD';
EXEC (#sql);
I would like to make the output either 1 or 0 instead of:
Processed 8584 pages for database 'DatabaseName', file 'DatabaseName.Data' on file 1.
Processed 1 pages for database 'DatabaseName', file 'DatabaseName' on file 1.
RESTORE DATABASE successfully processed 8585 pages in 3.929 seconds (17.899 MB/sec).
Is it possible to control this?
Using SQL Server 2005.

What does "output" mean? There are at least four ways to return information from a stored procedure: result set(s), output parameter(s), PRINT and the return code.
I don't think you can suppress the output, but you could put your RESTORE statement inside a TRY/CATCH block. If the RESTORE fails, return your failure code from the CATCH block, otherwise return success from the main procedure body.
Alternatively, don't use a stored procedure but an external script. I find that's actually much easier in many cases when working with backups and other operations that require you to manipulate filenames and even physical files. You could use Smo from a Powershell script, for example.

Related

xp_CmdShell , write image only to location where SQL Server is hosted..not to server(Location) i wants

I am using xp_CmdShell in an sp to write an image to a location like C:\ImageUpload. This works absolutely fine on the server location where SQL Server is hosted. I know this may be typical behaviour.
One of my clients however is having his own server where databases are hosted. He needs to write the image on another server location.
Below is part of my SQL sp. It normally works fine, but in the case above I want to write the image to another server location.
SET #strOutputFilePath = C:/ImageUpload
-- Get Final Path
SET #strOutputFilePath = #strOutputFilePath + #strFilename
PRINT #strOutputFilePath -- path will be like C:\ImageUpload\NameofFile
SET #strSql = 'BCP "set fmtonly off SELECT BlobImage FROM dbo.TblImage where ID = ' + CAST(#id AS VARCHAR(10)) +''
+ '" QUERYOUT ' + '"' +#strOutputFilePath+ '"' + ' -T -f C:\Test\testblob.fmt -S"' + ##SERVERNAME + '"'
PRINT #strSql
EXEC #RC = master.dbo.xp_CmdShell #strSql
PRINT #RC
How can I specify the particular server location so that the image can be written to that folder and not the one where SQL Server is hosted?
You need to do a few things.
update the server to use UNC path. so the reference to the location
should read \\ServerName\FileShare\File.txt rather and
Drive:\Directory\File.txt.
If the SQL server was installed correctly, and securely, the account
that xp_cmdshell uses might not have permissions. so either allow
the service account permissions to the shared folder OR (the more
secure method) setup a PROXY account for xp_cmdshell. You can find the account that xp_cmdshell uses by having it issue the command whoami

SQL Server Calling a stored procedure from another stored procedure at the command line

I have been playing around with database backup automation scripts and in particular the one at this link:
http://support.microsoft.com/kb/2019698
I got everything working fine and even added automated compression using 7zip, logging, and with the help of vbscript an email scheduled notification. However, even without all that, you can see this is a bit heavy. Its now easily reaching 400 lines of code.
I am really not comfortable having all my stuff in one block like this and I want to separate it out. So I can have say a compression file called BackupCompress.sql, and an log file called BackupLogReport.sql all of which would be called from inside the main Backup.sql script.
The Backup.sql script is in turn run from a Backup.bat file which is set to run in the scheduler.
All of this works like a charm. But I am at a loss as to how to call BackupCompress.sql from within BackupLogReport.sql and pass in parameters and get a return value.
In the Backup.bat file I use this command to spin everything up and pass parameters to it:
SQLCMD -S %SQLDATABASE% -d master -i %BACKUP_FOLDER%\Backup.sql -v Pram1="%Pram1%"
In the Backup.sql file I get those parameters simply by:
DECLARE #Param1 NVARCHAR(256) = '$(Param)'
from then on as my script runs it uses whatever I want to pass in.
I tried using standard sql stored procedure logic to call another procedure like this:
EXEC BackupCompress.sql
#AnotherParam = #Param1
I also tried:
EXECUTE sp_executesql BackupCompress.sql #Param1
Finally I tried:
SET #cmd = 'SQLCMD -S ' + ##ServerName + ' -d master -i $(BACKUP_FOLDER)\BackupCompress.sql -v Param1 = ' + #Param1
EXEC xp_cmdshell #cmd, no_output
but it doesn't work and my files which were being compressed simply don't get compressed. I get no error message. everything else continues to work fine.
EDIT: I was getting an error message on the last one but I fixed it - however, I still don't get my little zip file. I even put print's into the file to see if it was actually be executed but it does not seem to be.
EDIT2: Another option I have tried, almost works, but cant figure out how to pass parameters from within the sql file to the other file... As a result it generates an error saying it cant find the file as it's treating the path as a literal string instead of the variable value I want to pass.
:!!SQLCMD -S ##ServerName -d master -i #CFG_BACKUP_PATH\BackupCompress.sql -v Param1 = #Param1
xp_cmdshell can return values. These values can be captured into a table variable that you could use to "see" the results, and perhaps determine where the problem lies:
DECLARE #cmd VARCHAR(255);
DECLARE #Param1 NVARCHAR(256) = '$(Param)';
DECLARE #Results TABLE
(
ResultsText NVARCHAR(MAX)
);
SET #cmd = 'SQLCMD -S ' + ##ServerName + '-d master -i $(BACKUP_FOLDER)\$(BackupCompress.sql) -v Param1 = ' + #Param1;
SET #cmd = 'DIR \';
INSERT INTO #Results (ResultsText)
EXEC xp_cmdshell #cmd;
SELECT *
FROM #Results;
You need to ensure xp_cmdshell is enabled for the instance, by executing:
EXEC sp_configure 'xp_cmdshell',1;

SQL Server 2008 BCP export throws: NativeError = 945, Database cannot be opened due to inaccessible files or insufficient memory

I am trying to export to a CSV file using BCP like this:
DECLARE #SQL AS VARCHAR(1000)
DECLARE #CMD AS VARCHAR(8000)
SET #SQL='SELECT * from Pondor.dbo.tblLD'
SET #CMD = 'bcp "' + #SQL + '" QUERYOUT "' + #FILE + '" -c -t "," -U"UserA" -P"Password"
exec master..xp_cmdshell #cmd
This does not work and throws the following error:
SQLState = 37000, NativeError = 945
Database 'Pondor' cannot be opened due to inaccessible files or insufficient memory or disk space.
See the SQL Server errorlog for details.
Doing a SELECT * from Pondor.dbo.tblLD works and returns rows. Any ideas on what is wrong with the BCP command?
You don't specify a Server in your command. Add the -S option with the right value. If your server is the "Default" instance on your server you could put -S.
The steps to take to fix the database open error is explained by Pinal Dave
If possible add more hard drive space either by removing of unnecessary files from hard drive or add new hard drive with larger size.
Check if the database is set to Autogrow on.
Check if the account which is trying to access the database has enough permission to perform operation.
Make sure that .mdf and .ldf file are not marked as read only on operating system file system level.

What’s the easiest way to preview data from an image column?

I have some columns with image data type and I want to preview (or browse) the data in those tables. When I use Select top 1000 rows in SQL Server Management Studio, the value of image columns is displayed in hexadecimal. What’s the easiest way to preview those images since the hex-value is not useful to me?
PS.: database is not under my control, so changing data type is not an option.
If you have LinqPad installed, previewing images is simple. Query your record, convert the binary data to an image, then dump the output to the preview window.
Edit: If you aren't aware, LinqPad is a free utility that can be used for many things, such as a replacement for management studio. Most of the time I use it as a scratch pad for .Net for throw-away programs, test code, and samples.
var entity = // fetch data
using (var ms = new MemoryStream(entity.Image.ToArray()))
{
System.Drawing.Image.FromStream(ms).Dump();
}
Here's what the result looks like:
I would write a proc (or query; see below) to export the binary out to the file system and then use any old off the shelf photo management utility (i.e. Windows Photo Viewer) to take a look at what's inside.
If your clever in your file naming you could give yourself enough information about each image in the name to quickly find it in the database again once you've visually located what your looking for.
Here is a proc that will export binary to the file system. I modified from this sample code. It's untested but should be extremely close for concept. It's using BCP to export your binary. Check here for the full docs on the BCP utility.
The proc also gives you the ability to export everything in the table, or only a single row based on a the passed primarykey. It uses a cursor (yuck), as well as some dynamic sql (yuck, yuck) but sometimes you gotta do what you gotta do.
CREATE PROCEDURE ExportMyImageFiles
(
#PriKey INT,
#OutputFilePath VARCHAR(500)
)
AS
BEGIN
DECLARE #sql VARCHAR(8000)
IF #PriKey IS NULL /* export all images */
BEGIN
DECLARE curExportBinaryImgs CURSOR FAST_FORWARD FOR
SELECT 'BCP "SELECT MyImage FROM [dbo].[MyTable]
WHERE PrimaryKey =' + CAST(PrimaryKey AS VARCHAR(25)) +
'" queryout ' + #OutputFilePath + MyImageName + '.' +
MyImageType + ' -S MyServer\MyInstance -T -fC:\Documents.fmt'
FROM [dbo].[MyTable]
OPEN curExportBinaryImgs
FETCH NEXT FROM curExportBinaryImgs INTO #sql
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC xp_cmdshell #sql, NO_OUTPUT
FETCH NEXT FROM curExportBinaryImgs INTO #sql
END
CLOSE curExportBinaryImgs
DEALLOCATE curExportBinaryImgs
END
ELSE /* Export only the primary key provided */
BEGIN
SELECT #sql = 'BCP "SELECT MyImage FROM [dbo].[MyTable]
WHERE PrimaryKey =' + CAST(PrimaryKey AS VARCHAR(25)) +
'" queryout ' + #OutputFilePath
+ MyImageName + '.' + MyImageType +
' -S MyServer\MyInstance -T -fC:\Documents.fmt'
FROM [dbo].[MyTable]
WHERE PrimaryKey = #PriKey
EXEC xp_cmdshell #sql,NO_OUTPUT
END
END
This is all assuming of course that what is stored in your Image column is actually an image and not some other file type. Hopefully if it is an image you also know the type, bmp, jpg, png, gif, etc.
If you don't want the hassle or reusability of a full blown proc try single query like this:
DECLARE #OutputFilePath VarChar(500) = /* put output dir here */
DECLARE #sql VARCHAR(8000)
DECLARE curExportBinaryImgs CURSOR FAST_FORWARD FOR
SELECT 'BCP "SELECT MyImage FROM [dbo].[MyTable]
WHERE PrimaryKey =' + CAST(PrimaryKey AS VARCHAR(25)) +
'" queryout ' + #OutputFilePath + MyImageName + '.' +
MyImageType + ' -S MyServer\MyInstance -T -fC:\Documents.fmt'
FROM [dbo].[MyTable]
OPEN curExportBinaryImgs
FETCH NEXT FROM curExportBinaryImgs INTO #sql
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC xp_cmdshell #sql, NO_OUTPUT
FETCH NEXT FROM curExportBinaryImgs INTO #sql
END
CLOSE curExportBinaryImgs
DEALLOCATE curExportBinaryImgs
The image type isn't for storing images, it's just 'variable-length binary data'. This type is deprecated and you should now use varbinary(max) for variable length binary data.
Since the SQL Server has no knowledge of what type of binary data has been stored (.zip, .exe, .jpg, .anything) it's not surprising Management Studio doesn't provide a preview.
You definitely can't preview these data types in Managment Studio, but I like the solution given by #RTomas.
There is a really great add-in for SSMS SSMSBoost which which provides plenty of useful features, and of course the simplest way to preview images stored in SQL(at least in my opinion)
NOTE : You must restart SSMS after installing this add-in.
Install it and enjoy previewing images just with : RightClick > Visualize As > Picture
I don't know of a way to accomplish this in Management Studio. You'd probably be better server writing a simple application that can query the database and then convert the hex into the correct image type (.jpg, .png, etc). There are also commercial applications that will do this for you.
Using linqpad the code could become even simpler
Ones you have the entity/type with you on the binary data column you would see .ToImage() method
For my case I am looping through all the rows and publishing all binary columns to images.
Hope it helps.
var yourData_Or_List = // fetch data
DataItem_Or_ListItem.BinaryDataColumn.ToImage().Dump();

TransactSQL to run another TransactSQL script

I have 10 transact SQL scripts that each create a table and fill it with data.
I am attempting to create 1 master sql script that will run each of the 10 other scripts.
Is there a way with TSQL / TRANSACTSQL for Microsoft SQL Server 2008 to execute another tsql script from within the current tsql script?
This is intended to be run through the SQL Server Management Studio (SSMS).
Thanks!
Try this if you are trying to execute a .sql file in SSMS:
:r C:\Scripts\Script1.sql
:r C:\Scripts\Script2.sql
:r C:\Scripts\Script3.sql
...
note: for this to run turn on sql command mode (Query > SQLCMD Mode)
If these are scripts you run fairly often you might consider dropping them in a stored proc and running them that way...
You can also do it through sqlcmd (which I believe is more common):
sqlcmd -S serverName\instanceName -i C:\Scripts\Script1.sql
Or just use openrowset to read your script into a variable and execute it:
DECLARE #SQL varchar(MAX)
SELECT #SQL = BulkColumn
FROM OPENROWSET
( BULK 'MeinPfad\MeinSkript.sql'
, SINGLE_BLOB ) AS MYTABLE
--PRINT #sql
EXEC (#sql)
I find it useful to define a variable with the path, if I want to execute a set of scripts, say to run a test, something like:
:setvar path "C:\code\branch-qa"
:r $(path)\tables\client.sql
:r $(path)\tables\item.sql
:r $(path)\proc\clientreport.sql
exec clientreport
You can use osql or better yet the newer sqlcmd almost interchangeably. I am using osql in this example only because I happened to have a code sample sitting around but in production I am using sqlcmd. Here is a snipped of code out of a larger procedure I use to run update scripts against databases. They are ordered by major, minor, release, build as I name my scripts using that convention to track releases. You are obviously missing all of my error handing, the parts where I pull available scripts from the database, setup variables, etc but you may still find this snippet useful.
The main part I like about using osql or sqlcmd is that you can run this code in ssms, or in a stored procedure (called on a scheduled basis maybe) or from a batch file. Very flexible.
--Use cursor to run upgrade scripts
DECLARE OSQL_cursor CURSOR
READ_ONLY
FOR SELECT FileName
FROM #Scripts
ORDER BY Major, Minor, Release, Build
OPEN OSQL_cursor
FETCH NEXT FROM OSQL_cursor INTO #name
WHILE (##fetch_status <> -1)
BEGIN
IF ((##fetch_status <> -2) AND (#result = 0))
BEGIN
SET #CommandString = 'osql -S ' + ##ServerName + ' -E -n -b -d ' + #DbName + ' -i "' + #Dir + #name + '"'
EXEC #result = master.dbo.xp_cmdshell #CommandString, NO_OUTPUT
IF (#result = 0)
BEGIN
SET #Seconds = DATEDIFF(s, #LastTime, GETDATE())
SET #Minutes = #Seconds / 60
SET #Seconds = #Seconds - (#Minutes * 60)
PRINT 'Successfully applied ' + #name + ' in ' + cast(#Minutes as varchar)
+ ' minutes ' + cast(#Seconds as varchar) + ' seconds.'
SET #LastTime = GETDATE()
END
ELSE
BEGIN
SET #errMessage = 'Error applying ' + #name + '! The database is in an unknown state and the schema may not match the version.'
SET #errMessage = #errMessage + char(13) + 'To find the error restore the database to version ' + #StartingVersion
SET #errMessage = #errMessage + ', set #UpToVersion = the last version successfully applied, then run ' + #name
SET #errMessage = #errMessage + ' manually in Query Analyzer.'
END
IF #name = (#UpToVersion + '.sql')
GOTO CleanUpCursor --Quit if the final script specified has been run.
END
FETCH ENDT FROM OSQL_cursor INTO #name
END
The simplest way would be to make your scripts stored procedures, and to call (via the EXECUTE command) each procedure in turn from a central procedure. This is ideal if you're going to run the exact same script(s) over and over again (or the same script with different parameters passed in).
If your scripts are .sql (or any kind of text) file, as #Abe Miesller says (upvoted) you can run them from within SSMS via the :r command, when SQLCMD mode is enabled. You would have to know and script the exact file path and name. This cannot be done from within a stored procedure.
A last alternative, usable with "known" file names and necessary for arbitrary file names (say, all files currently loaded in a subfolder) is to leverage the power of extended procedure XP_CMDSHELL. Such solutions can get compelx pretty fast (use it to retrieve list of files, build and execute via xp_cmdshell a string calling SQLCMD for each file in turn, manage results and errors via output files, it goes on and on) so I'd only do this as a last resort.
Assuming you want to keep the 10 scripts in their own individual files, I would say the easiest way to do what you want would be to create a batch file that executes osql.exe to execute the 10 scripts in the order you want.