SQL server 2008: Copy a file and rename it - sql-server

I have a file inside a directory \\myServer\Admin\temp\testtemp.txt
I need to write a TSQL to
Search for testtemp.txt file.
If exists, create a copy of it and rename it to Copytesttemp.txt
If there's testtemp.txt already in the above directory like this
\\abcd\Admin\temp\Copytesttemp.txt
then delete it and recreate Copytesttemp.txt
How do I achieve it? Thanks.

You can use xp_cmdshell to run any DOS commands you like, e.g.
declare #cmdstring varchar(1000)
set #cmdstring = 'copy \\myServer\Admin\temp\testtemp.txt \\myServer\Admin\temp\Copytesttemp.txt'
exec master..xp_cmdshell #cmdstring
Just make sure xp_cmdshell is enabled on your installation.

Create a SQL Agent job which runs a command script to do the actions.

You can try this for Copy a file and rename
EXEC master..xp_cmdshell 'COPY D:\T1\a.txt D:\T2\b.txt'
Only copy and move just like CMD
EXEC master..xp_cmdshell 'COPY D:\T1\abcd.txt D:\T2'
EXEC master..xp_cmdshell 'Move D:\T1\abcd.txt D:\T2'

Related

Set variable in SQL equal to database name

I want to execute a .sql file by .bat file (where db=input name in cmd)
The top of the .sql file looks like this:
USE $(db)
DECLARE #Database varchar(500)
SET #Database = $(db)
But after executing the .bat file, an error occurs at line 4 of the .sql file
Apparently, variable = $(db) does not work.
Is there another way in order to do something like this?
Use SQLCMD variables, so change your batch file to call SQLCMD rather than ISQL
#echo off
sqlcmd -E -S myserver -i sample.sql -v db=YourDBName
sample.sql content would be like this
USE $(db)
DECLARE #Database varchar(500)
SET #Database = $(db)

Combine command line and T-SQL - SQL Server

I am trying to execute some sqlcmd through T-SQL on SQL Server 2008. There is a part of my code where I am checking a data file size and if that data file size does not equal to 0, then start deleting the specific table so I can BCP in new data.
Below is my code that is not being executed:
SET #myVariable = '
SETLOCAL
FOR %%R IN (X:\Main Folder\Data\'+#databaseName+'_'+#tableName+'.dat) DO SET size=%%~zR
IF %size% NEQ 0 (
SQLCMD -E -S my-server-name -Q "DELETE FROM '+#databaseName+'.'+#schemaName+'.'+#tableName+';" >> X:\Main Folder\Log\Log.txt
)'
EXEC master..xp_cmdshell #myVariable
For some reason when I execute my stored procedure, the code above seems to be skipped because it does not shoot back any error messages.
EDIT: After re-adjusting the spacing and my code, #myVariable, gets executed now. However, it still does not work in regards that it still deletes the table even though the data file size = 0. However, when I hard code it within a batch file, it works perfectly fine. Any ideas?
You need to use single % in your for loop as you are not executing the code in a batch file (that requires %%), see this post for some further clarification. So your for loop should be:
FOR %R IN (X:\Main Folder\Data\'+#databaseName+'_'+#tableName+'.dat) DO SET size=%~zR
I think the problem is that you're not using quotes around your filenames. That top level directory has a space in it.
Jaco's answer looks correct and I'm sure it was part of the problem. You should probably initialize size just to be safe too:
SET #myVariable = '
SETLOCAL
SET size=0
FOR %R IN ("X:\Main Folder\Data\'+#databaseName+'_'+#tableName+'.dat") DO SET size=%~zR
IF %size% NEQ 0 (
SQLCMD -E -S my-server-name -Q "DELETE FROM '+#databaseName+'.'+#schemaName+'.'+#tableName+';" >> "X:\Main Folder\Log\Log.txt"
)'
EXEC master..xp_cmdshell #myVariable
Without the quotes the for loop is going to treat that its "set" (the terminology used in for /?) as two items separated by the space. If your current directory is X:\Main Folder\Data\ it would still work though since it sees the last one as a relative path to the .dat file and then still sets the right value on the last pass.
Why would you go 'down' to the command line at all? (there are reasons why xp_cmdshell is disabled by default)
Could you not simply loop over your tables (sys.tables WHERE name LIKE ...) and then
create a shadow-copy of the table (SELECT INTO)
BULK INSERT from the (expected) file into shadow-table (in a TRY..CATCH to handle situations where the file does not exist, or is empty, or is corrupt, ..
if there is data in the shadow-table, then DELETE the actual table records and move the data over
if there is no data in the shadow table then DELETE the actual table records (or leave them in if you assume a missing or empty bcp file means it will arrive later on and you're stuck with the current version for now)
DROP the shadow table again
Not sure if this is an option for you but since you are on SQL 2008 you should be able to use powershell command instead:
DECLARE #File varchar(100), #outputFile varchar(100)
DECLARE #cmd varchar(1000)
SELECT #File = 'path_to_file'
SELECT #outputFile = 'path_to_output_file'
SELECT #cmd = 'powershell.exe -command "if((Get-Item '''+#File+''').length -gt 0) {&sqlcmd.exe -E -S SERVERNAME -Q ''SELECT name FROM master.sys.databases ;'' -o '+#outputFile+'}"'
SELECT #cmd
exec master..xp_cmdshell #cmd
I've checked and it seems to be working depending on the file size.
I can't speak to your DOS command. I can however suggest using Ole Automation Procedures to get the file size. That way you would not have to rely on running batch commands.
First you need to enable Ole Automation Procedures on your SQL Server instance, as follows:
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO
You only need to do this once.
Next is a script that gets the file size. The example assumes that there's a file called C:\Temp\testfile.txt. The script selects the size if the file exists, or selects 0 if it doesn't. You can take this script as an example to do what you want based on the size.
Here goes:
DECLARE #hr INT;
DECLARE #size INT;
DECLARE #obj_file INT;
DECLARE #obj_file_system INT;
DECLARE #file_name VARCHAR(100)='C:\Temp\testfile.txt';
-- Create a FileSystemObject. Create this once for all subsequent file manipulation. Don't forget to destroy this object once you're done with file manipulation (cf cleanup)
EXEC #hr = sp_OACreate 'Scripting.FileSystemObject', #obj_file_system OUT;
IF #hr<>0 GOTO __cleanup;
-- Get a handle for the file. Don't forget to release the handle for each file you get a handle for (see cleanup). The return will be different from 0 if the file doesn't exist
EXEC #hr = sp_OAMethod #obj_file_system, 'GetFile', #obj_file out, #file_name;
IF #hr<>0 GOTO __print_file_size;
-- Retrieve the file size.
EXEC sp_OAGetProperty #obj_file, 'size', #size OUT;
__print_file_size:
SELECT ISNULL(#size,0) AS file_size;
__cleanup:
EXEC sp_OADestroy #obj_file_system;
EXEC sp_OADestroy #obj_file;
You are using X:\ in your code. But the code is running under the service account for SQL Server. That account may not have x: available.
I would suggest using a UNC instead of a mapped drive. Also, make sure that your service is running under a domain account, and that the domain account has all required permissions to the UNC.
I realized that I can check the table count instead of a data file size by using this method:
SET #sqlCheck = 'SQLCMD -E -S ServerA -Q "IF (SELECT COUNT(*) FROM '+#databaseName+'.'+#schemaName+'.'+#tableName+') > 0 BEGIN DELETE FROM ServerB.'+#databaseName+'.'+#schemaName+'.'+#tableName+' END;"'
EXEC MASTER..xp_cmdshell #sqlcheck
You seems to know the names of the databases and tables already, so you can use the following, which basically does a DIR for the file you're looking for and checks if it's '0 bytes', if so it then does whatever you want. Things to note:
STRING TEMPLATES -- When building strings, I like to build a 'template' and then replace within the string. This is a good way to make sure you have the right number of quotes, parenthesis, etc. I did it twice here, once to build the DIR command and then again to build the TRUNCATE command.
TRUNCATE -- although not part if your question, you may want to use a TRUNCATE instead of DELETE FROM. If you had a million rows in your table, DELETE FROM may take 2 min to run, where as TRUNCATE will always take 0-seconds to run.
Your answer:
SET NOCOUNT ON;
DECLARE #DatabaseName VARCHAR(50) = 'database1'
DECLARE #TableName VARCHAR(50) = 'table1'
DECLARE #PathTemplate VARCHAR(50) = 'dir c:\temp\{#DatabaseName}_{#TableName}.txt'
SET #PathTemplate = REPLACE(#PathTemplate, '{#DatabaseName}', #DatabaseName);
SET #PathTemplate = REPLACE(#PathTemplate, '{#TableName}', #TableName);
DECLARE #FileNames AS TABLE (FileNames VARCHAR(100))
INSERT #FileNames (FileNames)
exec xp_cmdshell #PathTemplate
IF EXISTS ( SELECT 1 FROM #FileNames WHERE FileNames LIKE '%0 bytes')
BEGIN
PRINT 'No Content/Missing File'
END
ELSE BEGIN
DECLARE #SqlExc VARCHAR(500) = 'TRUNCATE TABLE [{#DatabaseName}].[dbo].[{#TableName}]'
SET #SqlExc = REPLACE(#SqlExc, '{#DatabaseName}', #DatabaseName);
SET #SqlExc = REPLACE(#SqlExc, '{#TableName}', #TableName);
PRINT #SqlExc
-- sp_executesql #SqlExc <-- Do this in production
END

Calling HTTP command from MS SQL

I had to migrate a Production SQL server from SQL 2008 to SQL 2012. The below piece of code was working fine with 2008. For some reason. it complains
"'HTTP' is not recognized as an internal or external command, operable
program or batch file."
Can someone please let me know what is going on ?
DECLARE #cmdstr VARCHAR(8000)
DECLARE #URL varchar(8000)
SET #URL = '"My_URL"'
CREATE TABLE #cmd_result (OUTPUT VARCHAR(8000))
EXEC master..xp_sprintf #cmdstr OUTPUT, 'HTTP /s %s ' ,#url
INSERT #cmd_result
EXEC master..xp_cmdshell #cmdstr
PRINT #cmdstr
SELECT * FROM #cmd_result WHERE len(rtrim(output)) > 1
DROP TABLE #cmd_result
you could run
EXEC master..xp_cmdshell 'dir http*'
and see if your http is showing up. chances are it is not or it needs to be added to the PATH environmental variable in order to access it from anywhere.

T-SQL writing to a file txt or csv

I've spent all day scouring the net on answers. Apparently tsql doesn't have its own nifty write to file commands. Here is my dilemma
I have a load file that I am creating where a single line can reach 10K+ in length. On SQL Server varchar(MAX) limit is 8000 (so I believe) so I broke those lines into several variables. I tried to do PRINT but the window pane has allows 4000. The workaround is to print those broken lines one variable at a time but that can get tedious for manual labor so I opted to look into writing it into a txt file one variable at a time.
I looked into BCP via xpcommandshell and it looked promising. Issue was that I could get this line to work on the command prompt yet that exact same line doesn't work on TSQL query:
declare #cmd varchar(8000)
select #cmd = 'bcp Client_DB "Select name from dbo.t_TagBuild_set" queryout "Desktop\LAMB\dummy.txt" -c -t, -T'
exec master..xp_cmdshell #cmd
bcp Client_DB "Select name from dbo.t_TagBuild_set" queryout "Desktop\LAMB\dummy.txt" -c -t, -T works perfectly fine on command prompt
despite this slight progress, my manager didn't want to go that route. So instead I opted for sp_OACreate and sp_OAMethod after enabling sp_configure via executing this line on SQL:
sp_configure 'Ole Automation Procedures', 1
One of the very first lines on this route is this:
EXECUTE #hr = sp_OACreate 'Scripting.FileSystemObject' , #objFileSystem OUT
#hr gives a 0 so that's good but #objFileSystem yields 16711422 and #hr eventually becomes -2146828218 which i believe is permissions.
i really am at a loss on finding something simple to do yet i've made this increasingly difficult on myself to find something concrete just to write to a couple variables in a row before adding a new line and repeat the process.
If anyone can expertly help me figure out BCP or sp_OACreate then I'd be very appreciative cause the net as is barely helps (and this is after I spent a lot of time looking through Microsofts own site for an answer)
The reason your BCP didn't work is because you were running it from xp_cmdshell with a trusted user. xp_cmdshell is not run under the user running the script. You can either a) change your bcp command to use a sql login/password or b) create a job to run it (not xp_cmdshell) because you can control what user it is run as by using run as and a credential. You can then launch the job within a script by using sp_start_job.
Your other good option is to create an SSIS package and either run it through the command line (say in a bat file) or again run it through a job.
Create a view of your query and select it using sqlcmd.
declare #cmd varchar(8000)
select #cmd = 'sqlcmd -h-1 -W -S servername -d database -U username -P password -Q "SET NOCOUNT ON; select * from VIEW_NAME " -o "C:\OUTPUT\query.csv" '
exec master..xp_cmdshell #cmd
-h-1 removes the header
SET NOCOUNT ON removes the rows affected footer
You can write to file on T-SQL using this (it works into trigger):
--char(9) = \t
DECLARE #filename nvarchar(1000);
SET #filename = ' (echo '+#parameterA+ char(9) +#parameterB+ ... + char(9) +#ParameterN+') > e:\file1.txt && type e:\file1.txt >> e:\file2.txt';
exec DatabaseName..xp_cmdshell #filename, no_output

How to store a sql table data into an XML file?

I have a situation where I have a data table in SQL server Database. Now i want it to be inserted into an XML File through SQL Commands. How can I do it?
You can execute query from cmd shell with -o parameter or the same command by xp_cmdshell. in select statement you can use for xml option. and results must be printed with print command.
for example:
SQLcmd -S "(local)\sqlexpress" -E -d "EFEx"
-q "declare #i nvarchar(max)
set #i = (select * from [Group] for xml auto,root(''groups''))
print #i"
-o "C:\Projs\results.xml"
don't forget security rights for creating file and enable xp_cmdshell
exec sp_configure 'xp_cmdshell', 1
reconfigure;
I hope this helps.
The short answer is no you can't output to a file using pure T-SQL. Whatever client you are using to connect to the SQL server needs to pipe the output to a file.
You have a few choices:
- BCP utility (recommended, read the XML format file section)
- SQLCMD utility
- Build an SSIS package to do the job (sounds like overkill for your case)
That said you can execute these commands from a SQL session using xp_cmdshell.

Resources