Im creating database which need few sql scripts. Running them one by one is not effective , so I wanted to create main sql query which will load all files. I tried this :
Declare
#DBServerName Varchar(100),
#DBName Varchar(100),
#FilePathName Varchar(100),
#strSql varchar(1000)
Set #DBServerName='USER-PC'-- Server Name
Set #DBName='testo' -- DB Name
Set #FilePathName='some path here'
Set #strSql= 'sqlcmd -S ' + #DBServerName + ' -d ' + #DBName + ' -i ' + #FilePathName
EXEC xp_cmdshell #strSql
After executing this, It is showing that my query was executed succesfully but in my database nothing is appearing. Ive seen lot of websites but there is no working answear.
I don't think you want xp_cmdshell here. If you want to execute dynamic SQL you should be using the code below:
sp_executesql #strsql;
I'm using Microsoft Sql server 2012 as DBMS, there I have my database which is containing informations about football players. Each player has a photography, and I need to export players photos to my computer, I guess it is possible to do it over TSQL, so I could avoid programming or editing my application to export photos only.
Here is what I did so far:
MY SQL CODE:
EXEC sp_configure 'show advanced options', 1 GO RECONFIGURE GO EXEC
sp_configure 'xp_cmdshell', 1 GO RECONFIGURE GO
EXEC master..xp_cmdshell 'mkdir C:\ImagesFromSql'
EXEC master..xp_cmdshell 'BCP "SELECT Photo FROM
[FootballTeam].[dbo].[Players]" queryout
"C:\ImagesFromSql\TestImage.jpg" -T -N'
As you can see, my folder ImagesFromSql is created on "C:\", also one photo is stored there but intersting is that photo is 361MB big so probably that is size of all photos that are contained in database?
And I am wondering how could I export all images from database instead of one in this case, and is it possible to set their names for example lets set Image name as PlayerID, for example 1.jpg, 2.jpg, 3.jpg....650.jpg...
after #H.Fadlallah suggested some answers this is what I get:
It is impossible to preview image.. :)
Again after #H.Fadlallah help I executed this query:
DECLARE #ID as int DECLARE #SQL as varchar(4000)
DECLARE csr CURSOR FOR SELECT PlayerID FROM
[FootballTeam].[dbo].[Players]
OPEN csr
FETCH NEXT FROM csr INTO #ID
WHILE ##FETCH_STATUS = 0 BEGIN
SET #SQL = 'BCP "SELECT Photo FROM [FootballTeam].[dbo].[Players] WHERE
PlayerID = "' + CAST(#ID as varchar(10)) + ' queryout
"C:\ImagesFromSql\' + CAST(#ID as varchar(10)) + '.jpg" -T -f
C:\ImagesFromSql\formatfile.fmt'
EXEC master..xp_cmdshell #SQL
FETCH NEXT FROM csr INTO #ID
END
CLOSE csr DEALLOCATE csr
But Now I'm getting empty images, probably I missed something.. (btw file formatfile.fmt is created in same folders as images and I changed binary value for my Photo attribute from 8 to 0 as H.Fadallah suggested me in another post).
try the following query:
EXEC sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'xp_cmdshell', 1
GO
RECONFIGURE
GO
EXEC master..xp_cmdshell 'mkdir C:\ImagesFromSql'
DECLARE #ID as int
DECLARE #SQL as varchar(4000)
DECLARE csr CURSOR FOR SELECT ID FROM [FootballTeam].[dbo].[Players]
OPEN csr
FETCH NEXT FROM csr INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'BCP "SELECT Photo FROM [FootballTeam].[dbo].[Players] WHERE ID = "' + CAST(#ID as varchar(10)) + ' queryout "C:\ImagesFromSql\' + CAST(#ID as varchar(10)) + '.jpg" -T -N'
EXEC master..xp_cmdshell #SQL
FETCH NEXT FROM csr INTO #ID
END
CLOSE csr
DEALLOCATE csr
I would like to execute multiple SQL (*.sql) TSQL script files, in SSMS, against a SQL Azure DB. I happen to be using SSMS 2008 R2
The code, I tried to execute in a SSMS query window, linked to the relevant DB instance, as picked up from a previous SO question, was :
/*
execute a list of .sql files against the server and DB specified
*/
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRAN
DECLARE #DBServerName VARCHAR(100) = 'DBServerName '
DECLARE #DBName VARCHAR(100) = 'DBName'
DECLARE #FilePath VARCHAR(200) = 'c:\temp\scripts'
/*
create a holder for all filenames to be executed
*/
DECLARE #FileList TABLE (Files NVARCHAR(MAX))
INSERT INTO #FileList VALUES ('script1.Sql')
INSERT INTO #FileList VALUES ('script2.Sql')
INSERT INTO #FileList VALUES ('script3.Sql')
WHILE (SELECT COUNT(Files) FROM #FileList) > 0
BEGIN
/*
execute each file one at a time
*/
DECLARE #FileName NVARCHAR(MAX) = (SELECT TOP(1) Files FROM #FileList)
DECLARE #command VARCHAR(500) = 'sqlcmd -S ' + #DBServerName + ' -d ' + #DBName + ' -i "' + #FilePath + #Filename +'"'
EXEC xp_cmdshell #command
PRINT 'EXECUTED: ' + #FileName
DELETE FROM #FileList WHERE Files = #FileName
END
COMMIT TRAN
Unfortunately SQL Azure does not support "xp_cmdshell".
How can I execute multiple SQL scripts for an Azure SQL instance?
Thanks in advance.
P.S I am aware of how one can open a file in SSMS
When using SSMS the script is send to the server and then executed there. As I mentioned in the comment in SQL Database the server does not have access to files on the machines they are hosted on nor can you actually upload them to these machines. So you approach does not work. Opening files that you have locally on your machine is also not possible.
To execute a set of scripts from your local machine against a SQL Database you have to translate your SQL script for example into a PowerShell script:
Get-ChildItem "c:\temp\scripts" -Filter script*.sql | `
Foreach-Object{
Write-Host 'Executing' $_.FullName
sqlcmd -U <username>#<server> -P <Password> -S <server>.database.windows.net -d <DatabaseName> -i $_.FullName
}
I have a stored proc that can write a file to a network drive using BCP, by creating a temporary drive on the database server that maps to the shared drive on another server. It is working correctly, however, I am returning an error from the last EXEC command, which says There are open files and/or incomplete directory searches pending on the connection to U:. I am guessing that it is trying to execute the delete drive command before it has finished writing the file. If I run the statement after running the proc, it will successfully delete the drive. Here is the proc:
CREATE PROCEDURE dn_ExportFile
#ServerName varchar(50),
#ServerPath varchar(500),
#FileName varchar(100),
#Query varchar(max),
#UserName varchar(100),
#Password varchar(100),
#Drive varchar(1) = 'U'
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cmd VARCHAR(8000)
--Set up virtual drive pointing to desired path
SET #cmd = 'NET USE ' + #Drive + ': ' + #ServerPath + ' /user:' + #ServerName + '\' + #UserName + ' ' + #Password
PRINT #cmd
EXEC xp_cmdshell #cmd
--Export data using BCP to virtual drive
SET #cmd = 'BCP "' + #Query + '" QUERYOUT "' + #Drive + ':\' + #FileName + '" -c -t -T'
PRINT #cmd
EXEC xp_cmdshell #cmd
--Delete virtual drive
SET #cmd = 'NET USE ' + #Drive + ': /delete'
PRINT #cmd
EXEC xp_cmdshell #cmd
END
Is there a way to successfully delete the temporary drive within the stored procedure?
I suspect it can't happen while the calling proc is still in scope. So you might try a wrapper stored procedure that does:
EXEC dbo.dn_ExportFile ...;
SET #cmd = 'NET USE ' + #Drive + ': /delete';
PRINT #cmd;
EXEC xp_cmdshell #cmd;
Otherwise I still think you're doing this in the wrong place. Have the program that calls this procedure call the command and dictate the path.
I am working on an application where I need to get the SQL response as XML into an XML file (and to store it in some physical location, say c:\xyz.xml).
I am able to generate the XML content using the provisions available in SQL Server as shown below.
SELECT * FROM #Table FOR XML AUTO, ELEMENTS
where: #Table is a table variable.
I want to know how I can store the query output to an XML file from SQL Server itself.
There's one more option - use sqlcmd tool.
Add :XML ON as a first line in your SQL file (let's call it input.sql)
A command like this will do the trick:
sqlcmd -S <your-server> -i input.sql -o output.xml
You need to use xp_cmdshell, and the bcp utility in the following way
EXEC xp_cmdshell 'bcp "SELECT * FROM #Table FOR XML AUTO, ELEMENTS" queryout "C:\table.xml" -c -T'
Hit me back in the comments if you've got any questions or want to know anything more about how this works.
You can't write to the file system from SQL Server itself. At least not that easily. There are three alternatives:
use xp_cmdshell. I would strongly advise against it. By default xp_cmdshell is disabled for security purposes, and to have it enabled it just for this operation opens a way to big security hole in your system.
use the FileSystemObject and the OLE Automation procedures sp_OACreate/sp_OAMethod. See Reading and Writing Files in SQL Server using T-SQL. This, while marginally better than the xp_cmdshell option, it doesn't give a much better security story. The only reason why is better than xp_cmdshell is that is by far less known by hackers. But the OLE Automation procedures option in SQL Server is also disabled by default and enabling this option exposes the very same security problems xp_cmdshell has.
use a CLR procedure. This would be my recommendation. Create an assembly with digital signature, use the assembly signature to allow, through Transact-SQL code signing, EXTERNAL ACCESS, then use the CLR procedure to write the XML into the file system. While this is significantly more complex than the simple xp_cmdshell or OLE Automation options, it is the most controlable and granular one from a security point of view and is the easiest to maintain and get right (is .Net code as opposed to shell scripts). Unfortunately, by default the clr option is also disabled in the server and has to be enabled.
If you press
ctrl + shift + f
you will have selected "Results To File." This can be found in the Query menu on the top bar of Sql Management Studio.
Or put something like this into your sql script
exec xp_cmdshell 'bcp "select * from suppliers" queryout "c:\suppliers.txt" -S server -T'
See this link, there is an issue about whether it is the app's c drive or the sql server's c drive. Have fun sorting that out.
You can create CLR function that create the file, build it into the sql server, and use it from a stored procedure
Another way( I haven't tested it ) - There is a tool bcp
bcp "Select * from dbo..table FOR XML RAW" queryout c:\temp\test.xml -Soc-db -Uuser -Ppassword
This example is from here
Simple SQL Write to File method
DECLARE #xml XML = '<MyXML></MyXMl>'
DECLARE #strXML varchar(max) = convert(varchar(max),#XML)
-- Add white space for readability
SELECT #strxml = replace(#strxml,'</',char(13) + char(10) + '</')
--- Add Declartives, namespaces and xls
Create Table dbo.BCP_OUT(contents varchar(max))
INSERT INTO dbo.bcp_out(contents)
SELECT Convert(varchar(max),#strXML )
EXEC xp_cmdshell N'BCP -S SERVER [database].dbo.bcp_out -T -c -o \\pathto\file.name'
If your xml output is relatively small (<4000 characters), then you can use this SP:
IF EXISTS (SELECT TOP 1 1 FROM sys.objects WHERE object_id = OBJECT_ID('dbo.USP_WRITE_UNICODE_STRING_TO_FILE') AND type = 'P')
BEGIN
DROP PROCEDURE dbo.USP_WRITE_UNICODE_STRING_TO_FILE
END
GO
-- =============================================
-- Description: Writes the specified Unicode string to the specified file.
-- Permissions: This stored procedure uses xp_cmdshell which is disabled by default. To enable it:
-- 1. In Management Studio connect to a component of SQL Server.
-- 2. In Object Explorer, right-click the server, and then click Facets.
-- 3. In the View Facets dialog box, expand the Facet list, and select the Surface Area Configuration.
-- 4. In the Facet properties area, select XPCmdShellEnabled property and set its value to True.
-- 5. Click OK.
-- Example: EXEC dbo.USP_WRITE_UNICODE_STRING_TO_FILE'<root> <a b="c" /> </root>', 'C:\Test.xml', 1;
-- =============================================
CREATE PROCEDURE dbo.USP_WRITE_UNICODE_STRING_TO_FILE
(
#Str NVARCHAR(4000),
#XmlFilePath NVARCHAR(256),
#Debug BIT = 0
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Str1 NVARCHAR(MAX),
#Cmd NVARCHAR(4000),
#MaxLen int = 4000;
--see: http://technet.microsoft.com/en-us/library/bb490897.aspx
SET #Str1 = REPLACE(REPLACE(REPLACE(#Str, '>', '^>'), '<', '^<'), '"', '^"');
-- '>' Writes the command output to a file
SET #Str1 =N'ECHO ' + #Str1 + N'>"'+ #XmlFilePath + N'"';
IF #Debug = 1
BEGIN
DECLARE #Msg varchar(128) = 'The total lenght is ' + CAST(LEN(#Str1) AS VARCHAR(10)) + ' characters.'
PRINT #Msg;
PRINT #Str1;
END
IF (LEN(#Str1) > #MaxLen)
RAISERROR ('The input string is too long', 11, 0);
ELSE
SET #Cmd = CAST (#Str1 AS NVARCHAR(4000));
EXEC master..xp_cmdshell #Cmd, NO_OUTPUT;
END
GO
--Test 1
DECLARE #Str NVARCHAR(4000);
DECLARE #Xml xml = '<root> <a b="c" /> </root>';
SET #Str = CAST (#Xml AS NVARCHAR(4000));
EXEC dbo.USP_WRITE_UNICODE_STRING_TO_FILE #Str, 'C:\Test.xml', 1;
GO
--Test 2
DECLARE #Str NVARCHAR(4000);
SET #Str = REPLICATE('a', 4000);
EXEC dbo.USP_WRITE_UNICODE_STRING_TO_FILE #Str, 'C:\Test.xml', 1;
GO
If you don't work with Unicode, then you can create another SP: USP_WRITE_NON_UNICODE_STRING_TO_FILE, which will be very similar to the previous one with the following changes:
CREATE PROCEDURE dbo.USP_WRITE_NON_UNICODE_STRING_TO_FILE
(
#Str VARCHAR(8000),
#XmlFilePath NVARCHAR(256),
#Debug BIT = 0
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Str1 VARCHAR(MAX),
#Cmd VARCHAR(8000),
#MaxLen int = 8000;
...
SET #Cmd = CAST (#Str1 AS VARCHAR(8000));
That SP allows the use of two times longer the input string (<8000 characters).
If your XML is longer than 8000 but less than 1MB you can use sqlcmd utility without :XML ON command. It greatly simplify the usage of the utility because you don't need a separate input_file with :XML ON command included. Here is an example:
DECLARE #Cmd NVARCHAR(4000);
SET #Cmd = N'sqlcmd -S ' + ##SERVERNAME + N' -d ' + DB_NAME() +
N' -Q "SET NOCOUNT ON; DECLARE #Xml xml = ''<root> <a >b</a> </root>''; SELECT CONVERT(NVARCHAR(MAX), #Xml);" -o "C:\Test.xml" -y 0';
PRINT #Cmd;
EXEC master..xp_cmdshell #Cmd, NO_OUTPUT;
You can also use an SP here:
CREATE PROCEDURE dbo.USP_SAMPLE_PROCEDURE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Xml xml;
SET #Xml = (SELECT name, type_desc FROM sys.objects FOR XML PATH('object'), ROOT('sys.objects'));
SELECT CONVERT(NVARCHAR(MAX), #Xml)
END
GO
DECLARE #Cmd NVARCHAR(4000);
SET #Cmd = N'sqlcmd -S ' + ##SERVERNAME + N' -d ' + DB_NAME() +
N' -Q "EXEC dbo.USP_SAMPLE_PROCEDURE;" -o "C:\Test.xml" -y 0';
PRINT #Cmd;
EXEC master..xp_cmdshell #Cmd, NO_OUTPUT;
GO
If your XML is more than 1MB you should use :XML ON command in a separate script and specify it as -i input_file parameter.
I made this SP so I can easily extract data from db or temp table to XML file on file system. It supports where clause.
CREATE PROCEDURE dbo.ExportToXMLFile
#TableName varchar(1000)
, #Where varchar(2000)=''
, #TicketNumber varchar(500)
, #debug bit=0
as
/*
Date:2016-03-27
Author: BojNed
Purpose: Exports data from table to XML file on filesystem.
#TableName = name of table to export.
#Where = optitional, to set #Where Clause. DO NOT ENTER WHERE at beggining of the string
#TicketNumber = To save to folder on filesystem
#Debug = Optitional. To debug this SP.
Examples:
EXEC dbo.ExportToXMLFile '#tt','columnX=2','221',0
EXEC dbo.ExportToXMLFile '[Production].[Product]','','252',1
EXEC dbo.ExportToXMLFile '[dbo].[DatabaseLog]','ColumnZ=55','351',0
EXEC dbo.ExportToXMLFile '[dbo].[DatabaseLog]','','7865',1
*/
begin
if #debug=0
SET NOCOUNT ON
declare #SQL nvarchar(max)
declare #IsTempTable bit
declare #NewTableName varchar(1000)
declare #Xml as XML
if (isnull(#TicketNumber,''))=''
begin
RAISERROR('No ticket number defined',16,1,1)
RETURN
END
--check if table is tmp or variable
if (SELECT SUBSTRING(#TableName,1,1))='#' or (SELECT SUBSTRING(#TableName,1,1))='#'
BEGIN
if #debug=1
PRINT 'Source is TMP table'
set #NewTableName='TMPTBL_'+#TableName
END
ELSE
BEGIN
if #debug=1
PRINT 'Source is db table'
set #NewTableName=replace(#TableName,'.','_')
END
--RemoveSpecialChars
declare #KeepValues varchar(1000)
set #KeepValues = '%[^a-z^0-9^_]%'
WHILE PATINDEX(#KeepValues,#NewTableName)>0
set #NewTableName = STUFF(#NewTableName, PATINDEX(#KeepValues,#NewTableName),1,'')
if #debug=1
PRINT 'Node name for XML Header and filename: '+#NewTableName
if ISNULL(#Where,'')=''
BEGIN
set #SQL= 'SELECT * FROM '+ #TableName+' FOR XML PATH, ROOT ('''+#NewTableName+'''), ELEMENTS'
if #debug=1
PRINT 'NO Where condition'
END
ELSE
BEGIN
set #SQL= 'SELECT * FROM '+ #TableName+' WHERE '+#Where+ ' FOR XML PATH, ROOT ('''+#NewTableName+'''), ELEMENTS'
if #debug=1
PRINT 'With Where condition'
END
--Get XML to tbl
if ISNULL(OBJECT_ID ('tempdb..##TXML'),0)>0
DROP TABLE ##TXML
CREATE TABLE ##TXML (XMLText XML)
set #SQL = ' insert into ##TXML select ('+#SQL+')'
--parse query
declare #testsql nvarchar(max)
declare #result int
set #testsql = N'set parseonly on; ' + #sql
exec #result = sp_executesql #testsql
-- If it worked, execute it
if #result = 0
begin
if #debug=1
PRINT 'Query OK: '+ #SQL
exec sp_executesql #sql
end
else
BEGIN
DECLARE #msg varchar(2000)
set #msg ='Parsing Error on query: ' + #SQL
RAISERROR (#msg,16,1,1)
RETURN
END
DECLARE #Tbl TABLE (id int identity(1,1), Dir varchar(256))
--check if dir exsists
INSERT into #Tbl
EXEC master.dbo.xp_subdirs 'C:\DataCorrectionBackup\'
if (SELECT Count(*) from #Tbl WHERE Dir=#TicketNumber)=0
BEGIN
--create new dir
DECLARE #t varchar(500)
set #t ='C:\DataCorrectionBackup\'+#TicketNumber
EXEC master.sys.xp_create_subdir #t
END
declare #bcp varchar(500)
declare #Filename VARCHAR(255)
set #Filename =convert(varchar(100),GETDATE(),112)+'_'+replace(convert(varchar(100),GETDATE(),114),':','')+'_'+#NewTableName+'.xml'
set #bcp = 'bcp "SELECT XMLText from ##TXML" queryout C:\DataCorrectionBackup\'+#TicketNumber+'\'+#Filename+' -w -T -S'+ ##servername
--save file
if #debug=0
EXEC xp_cmdshell #bcp, NO_OUTPUT
ELSE
BEGIN
EXEC xp_cmdshell #bcp
PRINT #bcp
END
DROP table ##TXML
end
go