How to Delete Directory from SQL Server? - sql

I have the following procedure which takes as parameter the name of database and the path and then creates a directory. My problem here I need override on the folder or delete it and then create a new one any idea of how to do such a thing
NOTE: this procedure creates in folder in depth of 0 and 1
ALTER PROCEDURE[dbo].[SP_CreateFolder]
#P_PATH VARCHAR(100),
#P_DBName VARCHAR(100)
AS
DECLARE #DBName sysname
DECLARE #DataPath nvarchar(500)
DECLARE #LogPath nvarchar(500)
DECLARE #DirTree TABLE (subdirectory nvarchar(255), depth INT)
-- Initialize variables
SET #DBName = #P_DBName
SET #DataPath = #P_PATH
-- #DataPath values
INSERT INTO #DirTree(subdirectory, depth)
EXEC master.sys.xp_dirtree #DataPath
SELECT * FROM #DirTree
-- Create the #DataPath directory
--IF (SELECT depth from #DirTree)!= 1
--EXEC master.dbo.xp_Delete_file #DBName
IF NOT EXISTS (SELECT 1 FROM #DirTree WHERE subdirectory = #DBName)
BEGIN
EXEC master.dbo.xp_create_subdir #DataPath
END
-- Remove all records from #DirTree
DELETE FROM #DirTree

First, the caveat: using T-SQL to operate on the file system is usually not a good idea, and there are a lot of risks assumed when doing it.
That said, you can use xp_cmdshell with the command-line syntax for whatever you want to do (deleting a folder, etc.), like this:
declare #rmdircmd nvarchar(280)
declare #dirname nvarchar(280)
set #dirname = '"C:\blah\blah\"'
set #rmdircmd = N'RMDIR ' + #dirname + ' /S /Q'
exec master.dbo.xp_cmdshell #rmdircmd, no_output
Good luck!

Related

Import multiple text files into SQL Server

I am using SQL Server 2017 Express edition on Microsoft Windows 10. I want to import all text files in a directory into a database at one time. My idea is that that SQL Server loops over the files in that directory and imports them all at the same time. Is there a way that I can achieve this?
Best Regards,
declare #dir varchar(1000) = 'C:\Temp';
declare #command varchar(1000);
declare #files table (id int identity, filename varchar(1000));
set #command = 'dir /b ' + #dir;
insert into #files execute xp_cmdshell #command;
declare commands cursor for
select 'bulk insert <your table name here> from ''' + #dir + '\' + filename + ''' <other options, WITH FORMAT, etc. here>;' from #files;
open commands;
fetch commands into #command;
while (##fetch_status = 0) begin
--print #command;
execute(#command);
fetch commands into #command;
end;
close commands;
deallocate commands;
Modify #dir and the bulk insert command that is being build and you're done.
You may have to enable 'xp_cmdshell', and this could be a problem for your DBA; and using 'execute' is always a potential issue (SQL injection, etc.).
To enable xp_cmdshell:
-- To allow advanced options to be changed.
EXEC sp_configure 'show advanced options', 1;
GO
-- To update the currently configured value for advanced options.
RECONFIGURE;
GO
-- To enable the feature.
EXEC sp_configure 'xp_cmdshell', 1;
GO
-- To update the currently configured value for this feature.
RECONFIGURE;
GO
As noted in another answer, xp_commandshell is problematic. SQL Server 2016+ allows another approach. See example
declare #tbl table(fn varchar(255), depth int,[isfile] int,primary key(fn))
declare #dir varchar(50)='C:\temp\' --'starting dir
insert #tbl
EXEC Master.dbo.xp_DirTree #dir,1,1 --dir, depth (1 - current only), file (0 - dirs only, 1+ - dirs and files)
delete #tbl where [isfile]=0 or fn not like '%.txt' --keep .txt files only
--select * from #tbl
--will insert into this table
create table #fileTbl (id int identity(1,1) primary key,fn varchar(255),txt varchar(max))
declare #fn varchar(255), #query nvarchar(4000)
select top 1 #fn=fn from #tbl
while ##ROWCOUNT>0--number of rows from the last query executed
begin
--dynamic query required to build OPENROWSET
set #query='insert #fileTbl(fn,txt) select '''+#dir+#fn+''',BulkColumn from openrowset(bulk ''c:\temp\'+#fn+''',single_blob) t'
exec sp_executesql #query
delete #tbl where fn=#fn
select top 1 #fn=fn from #tbl --proceed
end
select * from #fileTbl

bulk insert files with filenames that change everyday

I need to bulk insert a text file, which is always stored in the same folder. The file name is 'employee_date', where the date part is not always the actual date. It is a dynamic date that the user changes every day (with the format YYYYMMDDHHMM).
What I need is a query that bulk inserts the data in that text file (that is already formatted). My problem is bulk insert does not work with variables or with *.txt or employes*.txt.
I need a query that bulk insert only the file which name is like EMPLOYEE_YYYYMMDDHHMM.txt that can be executed every day and always insert the file from that folder, doesn't matter what the date is in the file name.
Here is something you can modify to fit your requirements. I had a similar task where we got files SFTP to us from a Linux system and I needed to upload this data into a SQL Server. Here's the basic layout... understanding your file locations, permissions on the folder, SQL permissions, etc all need to be taken into consideration. Including if you can run command shell code in your environment.
CREATE procedure [dbo].[file_upload]
as
DECLARE #dt VARCHAR(10) --date variable but stored as VARCHAR for formatting of file name
DECLARE #fileLocation VARCHAR(128) = 'E:\SomeFolder\' --production location which is
DECLARE #sql NVARCHAR(4000) --dynamic sql variable
DECLARE #fileName VARCHAR(128) --full file name variable
--This stores the file names into a temp table to be used in a cursor.
--The bottom part is handleing some date formatting i needed. You can change to what your files look like
IF OBJECT_ID('tempdb..#FileNames') IS NOT NULL DROP TABLE #FileNames
CREATE TABLE #FileNames (
id int IDENTITY(1,1)
,subdirectory nvarchar(512)
,depth int
,isfile bit
,fileDate date null
,fileTime time null)
INSERT #FileNames (subdirectory,depth,isfile)
EXEC xp_dirtree #fileLocation, 1, 1
UPDATE #FileNames SET
fileDate = CAST(SUBSTRING(subdirectory,LEN(subdirectory) - 19,10) AS DATE)
,fileTime = CAST(REPLACE(SUBSTRING(subdirectory,LEN(subdirectory) - 8,5),'-',':') AS TIME)
--here's the cursor to loop through all the files
DECLARE c CURSOR FOR
select subdirectory from #FileNames
OPEN c
FETCH NEXT FROM c INTO #fileName
--For each file, bulk insert or what ever you want...
WHILE ##FETCH_STATUS = 0
BEGIN
--set the dynamic with the appropriate delimiters, if you want to keep headers, etc.
SET #sql = 'BULK INSERT Server.dbo.someTable FROM '''+ #fileLocation + #fileName +''' WITH (FIELDTERMINATOR = ''|'',FIRSTROW=2,KEEPNULLS,ROWTERMINATOR = ''0x0a'')'
EXEC(#sql)
--do some other stuff like logging, updating, etc...
END
CLOSE c
DEALLOCATE c
This should do it for you. Just adjust as needed.
DECLARE #intFlag INT
SET #intFlag = 1
WHILE (#intFlag <=48)
BEGIN
PRINT #intFlag
declare #fullpath1 varchar(1000)
select #fullpath1 = '''your_path_here\employee_' + convert(varchar, getdate()- #intFlag , 112) + '.txt'''
declare #cmd1 nvarchar(1000)
select #cmd1 = 'bulk insert [dbo].[your_table_name] from ' + #fullpath1 + ' with (FIELDTERMINATOR = ''\t'', FIRSTROW = 2, ROWTERMINATOR=''0x0a'')'
exec (#cmd1)
SET #intFlag = #intFlag + 1
END
GO

Scripted Restore Using xp_DirTree For Transient Logical BAK File Name SQL Server

Hi I am trying to restore a DB from one server to another where the logical name of the .bak file changes daily with a new timestamp, I have so far found success in determining this name using the following SQL script provided by Jeff Moden here: http://www.sqlservercentral.com/Forums/Topic1200360-391-1.aspx
--===== Create a holding table for the file names
CREATE TABLE #File
(
FileName SYSNAME,
Depth TINYINT,
IsFile TINYINT
)
;
--===== Capture the names in the desired directory
-- (Change "C:\Temp" to the directory of your choice)
INSERT INTO #File
(FileName, Depth, IsFile)
EXEC xp_DirTree '\\filepath\',1,1
;
--===== Find the latest file using the "constant" characters
-- in the file name and the ISO style date.
SELECT TOP 1
FileName
FROM #File
WHERE IsFile = 1
AND FileName LIKE '%.bak' ESCAPE '_'
ORDER BY FileName DESC
;
DROP TABLE #File
My question is now how do I use this as the basis of a scripted restore operation? any help would be very much appreciated!
I have found success by extending the above to cache the directory path and ordered bak files chronologically to determine which to use, then combined the restore operation, with move for logs.
--==CHECK IF DB EXISTS IF IT DOES DROP IT
USE [master]
IF EXISTS(SELECT * FROM sys.databases where name='insert db name')
DROP DATABASE [insert db name]
--==START THE RESTORE PROCESS
DECLARE #FileName varchar(255), #PathToBackup varchar(255), #RestoreFilePath varchar(1000)
DECLARE #Files TABLE (subdirectory varchar(255), depth int, [file] int)
SET NOCOUNT ON
--==SET THE FILEPATH
SET #PathToBackup = '\\insert path to back up'
--insert into memory table using dirtree at a single file level
INSERT INTO #Files
EXEC master.dbo.xp_DirTree #PathToBackup,1,1
SELECT TOP 1
#FileName = [subdirectory]
FROM
#Files
WHERE
-- get where it is a file
[file] = 1
AND
--==FIND THE LOGICAL NAME OF THE BAK FILE FROM THE CHRONILOGICALLY ORDERED LIST
subdirectory LIKE '%.bak'
ORDER BY
-- order descending so newest file will be first by naming convention
subdirectory DESC
IF LEFT(REVERSE(#PathToBackup), 1) != '\'
BEGIN
SET #PathToBackup = #PathToBackup + '\'
END
SET #RestoreFilePath = #PathToBackup + #FileName
--Grab the file path to restore from
SELECT #RestoreFilePath
--BEGIN THE RESTORE TO THE DESIGNATED SERVER
RESTORE DATABASE [insert name of database to restore]
FROM DISK = #RestoreFilePath
WITH
FILE = 1,
--Create transactional log files on target
MOVE 'mdf_file_name' TO 'file_path\file.mdf',
MOVE 'log_file_name' TO 'file_path\file.ldf', REPLACE;
Here is a script that I have partly written and partly collected.
Features include:
Imports exactly one backup file that has any file name with .bak
ending. If there are more files, then imports only one without any
errors.
If no files exist, gives an error.
Kicks users out from the database.
Deletes the backup file
DECLARE #DBName nvarchar(255), #FileName nvarchar(255), #PathToBackup nvarchar(255), #RestoreFilePath nvarchar(1000)
DECLARE #Files TABLE (subdirectory nvarchar(255), depth int, [file] int)
SET XACT_ABORT, NOCOUNT ON
SET #PathToBackup = N'I:\Folder'
-- insert into our memory table using dirtree and a single file level
INSERT INTO #Files
EXEC master.dbo.xp_DirTree #PathToBackup,1,1
SELECT
#FileName = [subdirectory]
FROM
#Files
WHERE
-- get where it is a file
[file] = 1
AND
subdirectory LIKE N'%.bak'
ORDER BY
-- order descending so newest file will be first by naming convention
subdirectory DESC
IF LEFT(REVERSE(#PathToBackup), 1) != N'\'
BEGIN
SET #PathToBackup = #PathToBackup + N'\'
END
SET #RestoreFilePath = #PathToBackup + #FileName
SET #DBName = LEFT(#FileName, LEN(#FileName)-4)
-- SELECT 'Replace AdventureWorks2016CTP3 in this script with #DBName'
SELECT #RestoreFilePath
BEGIN TRY
-- You can try to check if this command works "already":
-- ALTER DATABASE [#DBName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
ALTER DATABASE [AdventureWorks2016CTP3] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
-- You can try to check if this command works "already":
-- RESTORE DATABASE [#DBName]
RESTORE DATABASE [AdventureWorks2016CTP3]
FROM DISK = #RestoreFilePath
WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 10
END TRY
BEGIN CATCH
-- You can try to check if this command works "already":
-- ALTER DATABASE [#DBName] SET MULTI_USER;
ALTER DATABASE [AdventureWorks2016CTP3] SET MULTI_USER;
; THROW
END CATCH
-- You can try to check if this command works "already":
-- ALTER DATABASE [#DBName] SET MULTI_USER;
ALTER DATABASE [AdventureWorks2016CTP3] SET MULTI_USER;
-- This script is especially for the case where you replication from one location to another using backup and restore.
-- Typically you don't need transaction log backups as all changes will be wiped out on next transfer.
-- You can try to check if this command works "already":
-- ALTER DATABASE [#DBName] SET RECOVERY SIMPLE;
ALTER DATABASE [AdventureWorks2016CTP3] SET RECOVERY SIMPLE;
-- Delete file(s)
-- NOTE: This works only if you give deletion permissions as defined in https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/xp-cmdshell-server-configuration-option?view=sql-server-2017
-- EXAMPLE: exec xp_cmdshell 'del "I:\Directory\AdventureWorks2016CTP3___.bak"'
-- exec xp_cmdshell 'del "' + '#PathToBackup + ''\'' + #FileName + ''"''
DECLARE #cmd NVARCHAR(MAX) = 'xp_cmdshell ''del "' + #PathToBackup + #FileName + '"''';
-- SELECT #cmd
EXEC (#cmd)

Delete backup files older than 7 days

Using osql command, SSQL backup of a database is created.
It is saved to Disk.
Then renamed to match the date of the day backup was taken.
All these files are saved in a single folder all the time.
for example:
Batch1.bat does the following
1) Created backup.bak
2) renamed to backup 12-13-2009.bak (this is done by the combination of % ~ - etc to get the date parameter)
This is now automated to take backup everyday by Task scheduler in Windows.
Can the batch file also be modified to delete backup files older than 7 days? if so, how ?
If it is not possible via batch file, other than manually deleting the files are there any other alternatives to automate the deleting job ?
Thanks in advance, Balaji S
If you have RoboCopy (part of the Windows Server 2003 Resource Kit Tools) installed.
The following lines can be added to your Batch1.bat file to move and delete files older than seven days:
ROBOCOPY C:\Temp\New C:\Temp\Old *.* /move /minage:7
DEL C:\Temp\Old\*.*
The first row moves all files that are older than seven days in the 'New' folder to the 'Old' folder.
The second row deletes all files in the 'Old' folder
I'm using the same technique to make a backup from database. I've created a stored procedure as follows.
Create Procedure [dbo].[CreateBackup]
As
Begin
Declare #path nvarchar(256),
#filename nvarchar(256),
#currentDateTime datetime
Set #currentDateTime = GetDate()
Set #path = 'C:\DBBackup\'
Set #filename = #path + Cast(DatePart(month, #currentDateTime) As nvarchar) + Cast(DatePart(day, #currentDateTime) As nvarchar) + '.bak'
Backup Database Foo To Disk = #filename
Set #currentDateTime = DateAdd(day, -3, #currentDateTime)
Set #filename = 'del ' + #path + Cast(DatePart(month, #currentDateTime) As nvarchar) + Cast(DatePart(day, #currentDateTime) As nvarchar) + '.bak'
Exec xp_cmdshell #filename
End
To use the xp_cmdshell, you should enable it before.
http://weblogs.sqlteam.com/tarad/archive/2006/09/14/12103.aspx
enter code hereThis procedure backup given database(s) for specified #retentionPeriod to a given network location (or local)
USE [master]
GO
/****** Object: StoredProcedure [dbo].[scrDoRemoteBackup] Script Date: 12/13/2009 09:20:20 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER proc [dbo].[scrDoRemoteBackup] as
set NoCount on
declare #dbName Varchar(100)
,#fName varchar(100)
,#RetentionPeriod Int -- in days
,#i int
,#n int
,#NetPath varchar(400)
,#BackupSetName Varchar(300)
,#params nvarchar(300)
,#q nvarchar(3400)
,#cmd nvarchar(4000)
,#error int
Declare #DataBases2Backup table(id int identity(1,1),dbName Varchar(100))
-- total back time 4:24 2nd Nov 06
set #NetPath='\\rapidsnet01s\RapidsDbBackup'
set #netPath='\\mail1\RapidsDbBackup'
set #netPath = '\\rapidsweb02p\Backup'
set #netPath='\\BUP2D-2K\_RapidsDB$\SAABZXO1D' -- 26th Feb 2008 - ap/li rapidsweb02p\backup space runout
set #netPath='\\saacsfs\RapidsBackup\SAABZXo1d' -- 22nd Oct 2009
---- insert all databaes to be backed up
--Insert #DataBases2Backup select 'Rapids'
--Insert #DataBases2Backup select 'GDC'
--Insert #DataBases2Backup select 'Master'
--
--
----Insert #DataBases2Backup select 'GDCComDev'
--
--Insert #DataBases2Backup select 'saaNCP'
----Insert #DataBases2Backup select 'US_Reps'
--Insert #DataBases2Backup select 'saaPackageWorx'
--Insert #DataBases2Backup select 'saaExtract'
Insert #DataBases2Backup select 'Master'
Insert #DataBases2Backup select 'QuickPickDBprod'
Insert #DataBases2Backup select 'QuickPickDBStaging'
--Set #RetentionPeriod = 13
Set #RetentionPeriod = 6 -- For Terry McCraith Jan06'09
select #n= count(*) from #DataBases2Backup
set #i = 1;
-- Set the Network path for the Backup location ;
-- ( re-establish the connection if the connection was broken)
set #q = 'net use '+#Netpath --+' * /USER:apeiris#armlink.com'
print #q
exec master.dbo.xp_cmdShell #q
While #i <= #n
Begin
select #dbName=dbName from #DataBases2Backup where id = #i
-- Get the backupset name prior to Retention Period
set #BackupSetName=#dbName+ dbo.fnGetDateNameBefore(GetDate(),#RetentionPeriod)
-- Delete the old backup device
set #q='Del '+#NetPath+'\'+#BackupSetName+'.bkp'
exec master.dbo.xp_cmdShell #q
-- Now get the current backupset name and backit up
set #BackupSetName=#dbName+ dbo.fnGetDateNameBefore(GetDate(),0)
set #fname = #netPath +'\'+#BackupSetName+'.bkp'
print 'Fname ='+#fname
Print 'Drop and Add Dumpdevice ' + #dbname + ' Fname='+#fname
exec #error=sp_dropDevice #dbname
print 'Error drop device-----------'+Convert(varchar,#error)+':'+Convert(varchar,##error)
exec #error=sp_addumpDevice 'disk',#dbName,#fname
exec sp_helpdevice #dbName
print 'Error -----------'+Convert(varchar,#error)
set #i=#i+1
if #error <> 0
Begin
print 'send alert'
exec saabzt01p.alerts.dbo.prDispatchAlertV2 'RemoteDBBackupError' ,##servername,'test','RemoteDBBackupError','test'
End
else
Begin
Backup Log #dbname with truncate_only
Backup Database #dbname to #dbname with format
if ##Error <> 0
Begin
print 'Send alert'
exec saabzt01p.alerts.dbo.prDispatchAlertV2 'RemoteDBBackupError',##servername,'test','RemoteDBBackupError','test'
Goto Errors
end
end
End
Errors:
And here are supporting functions
-- This function returns oldest data set name compared to retentionPeriod
Create function [dbo].[fnGetDateNameBefore](#d Datetime,#nDays int) returns varchar(40) as
Begin
declare #OD datetime
,#dName Varchar(40);
set #OD=DateAdd(dd,#nDays * -1,#d)
select #dName= DateName(yy,#od)+Left(DateName(Month,#od),3)+DateName(dd,#od)
Return #dName
end
-- This function returns oldest data set name compared to retentionPeriod
Create function [dbo].[fnGetDateNameBefore](#d Datetime,#nDays int) returns varchar(40) as
Begin
declare #OD datetime
,#dName Varchar(40);
set #OD=DateAdd(dd,#nDays * -1,#d)
select #dName= DateName(yy,#od)+Left(DateName(Month,#od),3)+DateName(dd,#od)
Return #dName
end
Nope don't do it that way. Do it this way.
Use SQL backup manager from the command line and script out a schedlue using Backup manager express. This is included and located in your SQL bin folder. Run this once, then modify the registry if it gives you an error. Then run again, ot should work. Perform this methodolgy to obtian your backups on a scheduled basis.

Generating XML file from SQL Server 2008

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