Related
I currently have a variable name called InvoiceFileName that is creating .csv files through a foreach loop. A list of .csv is then outputted to a folder.
I will then need to query off of each .csv file to select the header and the first row of data for each .csv.
I believe I need to use the OPENROWSET to query off of the .csv. I have 2 questions.
What is the syntax to query off of the variable name InvoiceFileName.
Is it possible to select the header field and first row of data OPENROWSET without inserting into a table.
Below is a simple OPENROWSET that only provides the header of the file.
SELECT
top 1 *
FROM OPENROWSET(BULK N'\\myservername\f$\reports\Invoices\CokeFiles\54ASBSd.csv', SINGLE_CLOB) AS Report
What kind of privs do you have on the database? If you have or can get slightly elevated privs, you can use BULK INSERT and xp_cmdShell to accomplish this, but like #scsimon said, you will have to use dynamic sql. Here's a quick example:
-----------------------------------------------------------------------------------------------------
-- Set up your variables
-----------------------------------------------------------------------------------------------------
DECLARE
#folderPath AS VARCHAR(100) = '\\some\folder\path\here\',
#cmd AS VARCHAR(150), -- Will populate this with a command to get a list of files in a directory
#InvoiceFileName AS VARCHAR(100), -- Will be used in cursor loop
#targetTable AS VARCHAR(50) = 'SomeTable',
#fieldTerminator AS CHAR(1) = ',',
#rowTerminator AS CHAR(2) = '\n'
-----------------------------------------------------------------------------------------------------
-- Create a temp table to store the file names
-----------------------------------------------------------------------------------------------------
IF OBJECT_ID('tempdb..#FILE_LIST') IS NOT NULL
DROP TABLE #FILE_LIST
--
CREATE TABLE #FILE_LIST(FILE_NAME VARCHAR(255))
-----------------------------------------------------------------------------------------------------
-- Get a list of the files and store them in the temp table:
-- NOTE: this DOES require elevated permissions
-----------------------------------------------------------------------------------------------------
SET #cmd = 'dir "' + #folderPath + '" /b'
--
INSERT INTO #FILE_LIST(FILE_NAME)
EXEC Master..xp_cmdShell #cmd
--------------------------------------------------------------------------------
-- Here we remove any null values
--------------------------------------------------------------------------------
DELETE #FILE_LIST WHERE FILE_NAME IS NULL
-----------------------------------------------------------------------------------------------------
-- Set up our cursor and loop through the files
-----------------------------------------------------------------------------------------------------
DECLARE c1 CURSOR FOR SELECT FILE_NAME FROM #FILE_LIST
OPEN c1
FETCH NEXT FROM c1 INTO #InvoiceFileName
WHILE ##FETCH_STATUS <> -1
BEGIN -- Begin WHILE loop
BEGIN TRY
-- Bulk insert won't take a variable name, so dynamically generate the
-- SQL statement and execute it instead:
SET #sql = 'BULK INSERT ' + #targetTable + ' FROM ''' + #InvoiceFileName + ''' '
+ ' WITH (
FIELDTERMINATOR = ''' + #fieldTerminator + ''',
ROWTERMINATOR = ''' + #rowTerminator + ''',
FIRSTROW = 1,
LASTROW = 2
) '
EXEC (#sql)
END TRY
BEGIN CATCH
-- Handle errors here
END CATCH
-- Continue your loop
FETCH NEXT FROM c1 INTO #path,#filename
END -- End WHILE loop
-- Do what you need to do here with the data in your target table
A few disclaimers:
I have not tested this code. Only copied from a slightly more complex proc I've used in the past that works for exactly this kind of scenario.
You will need elevated privs for BULK INSERT and xp_cmdShell.
I know people frown on using xp_cmdShell (and for good reason) but this is a quick and dirty solution making a lot of assumptions about what your environment is like.
This is assuming you're not grabbing the data as you get each file in your variable. If you are, you can skip the first part of this code.
This code also assumes you are doing your own error handling in places other than the one try/catch block you see. I've omitted a lot of that for simplicity.
For doing this through SSIS, ideally you'd probably need to use a format file for the bulk operation, but you'd have to have consistently formatted files and remove the SINGLE_CLOB option as well. A really hacky and non-ideal way to do this would be to do something like this:
Let's say your file contains this data:
Col1,Col2,Col3,Col4
Here's,The,First,Line
Here's,The,Second,Line
Here's,The,Third,Line
Here's,The,Fourth,Line
Then you could basically just parse the data doing something like this:
SELECT SUBSTRING(OnlyColumn, 0, CHARINDEX(CHAR(10), OnlyColumn, CHARINDEX(CHAR(10), OnlyColumn, 0)+1) )
FROM OPENROWSET(BULK '\\location\of\myFile.csv', SINGLE_CLOB) AS Report (OnlyColumn)
And your result would be this:
Col1,Col2,Col3,Col4 Here's,The,First,Line
This is obviously dependent on your line endings being consistent, but if you want the results in a single column and single row (as is the behavior of the bulk operation with the SINGLE_CLOB option), that should get you what you need.
You can take a look at the solution on this SO post for info on how to pass the SSIS variable value as a parameter to your query.
Use a Foreach Loop container to query all files in a folder. You can use wildcards for the file name, or user the variables in your DTS to set the properties of the components.
Inside the loop container you place a Data Flow Task with your source file connection, your transformations, and your destination.
You can modify the file names and paths of all these objects by setting their properties to variables in your DTS.
With an Expresion Task inside the loop, you can change the path of the CSV file connection.
I know my post has a very similar title to other ones in this forum, but I really couldn't find the answer I need.
Here is my problem, I have a SQL Server running on my Windows Server. Inside my SQL Server, I have around 30 databases. All of them have the same tables, and the same stored procedures.
Now, here is the problem, I have this huge script that I need to run in all of these databases. I wish I could do it just once against all my databases.
I tried a couple things like go to "view" >> registered servers >> local server groups >> new server registration. But this solution is for many servers, not many databases.
I know I could do it by typing the database name, but the query is really huge, so it would take too long to run in all databases.
Does anybody have any idea if that is possible?
You can use WHILE loop over all database names and inside loop execute query with EXECUTE. I think that statement SET #dbname = ... could be better, but this works too.
DECLARE #rn INT = 1, #dbname varchar(MAX) = '';
WHILE #dbname IS NOT NULL
BEGIN
SET #dbname = (SELECT name FROM (SELECT name, ROW_NUMBER() OVER (ORDER BY name) rn
FROM sys.databases WHERE name NOT IN('master','tempdb')) t WHERE rn = #rn);
IF #dbname <> '' AND #dbname IS NOT NULL
EXECUTE ('use '+QUOTENAME(#dbname)+';
/* Your script code here */
UPDATE some_table SET ... ;
');
SET #rn = #rn + 1;
END;
Consider running the script in SQLCMD Mode from SSMS (Query--SQLCMD Mode). This way, you can save the script to a file and run it in the context of each of the desired databases easily:
USE DB1;
:r C:\SqlScript\YourLargeScript.sql
GO
USE DB2;
:r C:\SqlScript\YourLargeScript.sql
GO
USE DB3;
:r C:\SqlScript\YourLargeScript.sql
GO
This technique can also be used to run the script against databases on other servers with the addition of a :CONNECT command. The connection reverts back to initial server/database after execution of the entire script:
:CONNECT SomeServer
USE DB4;
:r C:\SqlScript\YourLargeScript.sql
GO
:CONNECT SomeOtherServer
USE DB5;
:r C:\SqlScript\YourLargeScript.sql
GO
Important gotcha: Note GO batch separators are needed for :CONNECT to work as expected. I recommend including GO in the the invoking script like the above example but GO as the last line in the :r script file will also provide the desired results. Without GO in this example (or at the end of the script file), the script would run twice on SomeServer and not run against SomeOtherServer at all.
ApexSQL Propagate is the tool which can help in this situation. It is used for executing single or multiple scripts on multiple databases, even multiple servers. What you should do is simply select that script, then select all databases against which you want to execute that script:
When you load scripts and databases you should just click the “Execute” button and wait for the results:
You can write script like this
DECLARE CURSOR_ALLDB_NAMES CURSOR FOR
SELECT name
FROM Sys.Databases
WHERE name NOT IN('master', 'tempdb')
OPEN CURSOR_ALLDB_NAMES
FETCH CURSOR_ALLDB_NAMES INTO #DB_NAME
WHILE ##Fetch_Status = 0
BEGIN
EXEC('UPDATE '+ #DB_NAME + '..SameTableNameAllDb SET Status=1')
FETCH CURSOR_ALLDB_NAMESINTO INTO #DB_NAME
END
CLOSE CURSOR_ALLDB_NAMES
this is the normal way of doing this :
suppose you want to do a select on database DBOther than it would be :
select * from DBOther..TableName
Also check if the table or view is on the dbo schema, if not you should add the schema also : Please notice I use only one dot now after the database name
select * from DBOther.dbo.ViewName
If any of the databases is on another server on another machine, than make sure the Database is in the Linked Server.
Then you can access the table or view on that database via:
SELECT * FROM [AnotherServerName].[DB].[dbo].[Table]
Here is another way that does not requires typing the database name :
use DB1
go
select * from table1
go
use DB2
go
select * from table1
go
Note that this will only work if the tables and fields are exact the same on each database
You can use the following script to run the same script on a set of databases. Just change the filter in the insert line.
declare #dbs table (
dbName varchar(100),
done bit default 0
)
insert #dbs select [name], 0 FROM master.dbo.sysdatabases WHERE [Name] like 'targets_%'
while (exists(select 1 from #dbs where done = 0))
begin
declare #db varchar(100);
select top 1 #db = dbName from #dbs where done = 0;
exec ('
use [' + #db + '];
update table1 set
col1 = '''',
col2 = 1
where id = ''45b6facb-510d-422f-a48c-687449f08821''
');
print #db + ' updated!';
update #dbs set done = 1 where dbName = #db;
end
If your SQL Server version does not support table variables, just use Temp Tables but don`t forget to drop them at the end of the script.
Depending on the requirement, you can do this:
declare #dbName nvarchar(100)
declare #script nvarchar(max)
declare #dbIndex bigint = 0
declare #dbCount bigint = (
select count(*) from
sys.databases
)
declare crs_databases cursor for
(
select
[name]
from
sys.databases
)
open crs_databases
fetch next from crs_databases into #dbName
while ##FETCH_STATUS = 0
begin
set #dbIndex = #dbIndex+1
set #script = concat(#script,
' select Id from ['+#dbName+']..YourTableName ',
case
when #dbIndex = #dbCount then ''
else 'union'
end)
fetch next from crs_databases into #dbName
end
select #script
close crs_databases
deallocate crs_databases
Please note that the double dotted notation assumes that the schema is dbo. Otherwise, you need to explicitly write down the schema.
select Id from ['+#dbName+'].schema.YourTableName
When you need to execute stored procedures on each server, the #script variable will have another content.
I am setting up a SQL script that creates a database from a variable name, and then takes that newly created database and restores it from a .bak file. I am having issues with some syntax in one of my commands that I am setting up, and wanted to ask if anybody could help me spot my syntax error? I am only going to paste my troubled snippet of code and its declarations, and if I am correct the issue lies in the way that I am declaring the file name paths. I have tried setting the paths to variables, but I still received errors due to the apostrophe placement. Thanks!!!
declare #DBname varchar(10), #sqlcommand Nvarchar(max)
set #DBname = 'testdb'
Code to create database, and set new database to single user mode
--restore database
set #sqlcommand = N'Restore DATAbase ' + #DBname + ' from disk = ''C:/loc_smartz_db0_template.bak'' with move '
+ #DBname + ' to ''C:/ProgramFiles/Microsoft SQL Server/MSSQL/Data/TestDatabase1.mdf'', move ' + #DBname + ' to ''C:/ProgramFiles/Microsoft SQL Server/MSSQL/Data/TestDatabase1.ldf'', Replace'
EXECUTE sp_executesql #sqlcommand
Code that sets database back to multiuser, and prints that the database was successfully created
It looks like the previous posters have fixed your problem, but this may have been avoided if you had used dynamic sql in the 'best practice' manner. Concatenating the string together as a mixture of variables and string literals is not ideal as it makes working with apostrophes difficult (as shown here).
A better way is to write your sql as
declare #DBname nvarchar(255) = 'testdb'
,#BakName nvarchar(255) = 'C:\loc_smartz_db0_template.bak'
,#MovemdfName nvarchar(255) = 'C:\Program Files\Microsoft SQL Server\MSSQL\Data\TestDatabase1.mdf'
,#MoveldfName nvarchar(255) = 'C:\Program Files\Microsoft SQL Server\MSSQL\Data\TestDatabase1.ldf'
,#sqlcommand nvarchar(max)
,#paramList nvarchar(max)
set #paramList = '#DBname nvarchar(255), #BakName nvarchar(255), #MovemdfName nvarchar(255), #MoveldfName nvarchar(255)'
set #sqlcommand = N'Restore DATAbase #DBname from disk = #BakName with move #DBname to #MovemdfName, move #DBname to #MoveldfName, Replace'
exec sp_executesql #statement = #sqlcommand
,#params = #paramList
,#DBname = #DBname
,#BakName = #BakName
,#MovemdfName = #MovemdfName
,#MoveldfName = #MoveldfName
This way, your sql command is very easy to read and maintain. Note that you don't have to mess around with escaping the apostrophes in the variable values either if you have spaces in your pathnames.
It also has the advantage (if you have the code in a stored proc) of allowing SQL Server to reuse execution plans which will improve performance.
See here for more information.
Two things.
First, you have to put single quotes around the database file logical name, e.g.
from
...with move testdb to 'C:/ProgramFiles/Microsoft SQL Server/MSSQL/Data/TestDatabase1.mdf'
to
...with move 'testdb' to 'C:/ProgramFiles/Microsoft SQL Server/MSSQL/Data/TestDatabase1.mdf'
making it
set #sqlcommand = N'Restore DATAbase ' + #DBname + ' from disk = ''C:\loc_smartz_db0_template.bak'' with move '''
+ #DBname + ''' to ''C:\ProgramFiles\Microsoft SQL Server\MSSQL\Data\TestDatabase1.mdf'', move '''
+ #DBname + ''' to ''C:\ProgramFiles\Microsoft SQL Server\MSSQL\Data\TestDatabase1.ldf'', Replace'
Second, use backslashes \, not slashes. (Maybe this works, but it didn't in my quick tests.)
Actually, this is a 2 part question.
Is it possible to use some sort of functionality to search through every stored procedure for a string and possibly replace it, like a standard Find/Replace function?
If you have all your stored procedure code include the full database path like this [db1].[dbo].[table1] and you change the database name to [db2] is there a way for SQL Server to automatically update all the code from [db1] tables to [db2]? Or does it have to be done manually?
From the Object Explorer Details window in SSMS, open the stored procedures folder. Select all the objects (you can multi-select from this window, which is pretty much the only purpose of the Object Explorer Details window) and right click, choosing to script as DROP and CREATE. You can now do a search/replace on this, replacing all you need in one go before executing it.
Edit: I've blogged about this solution.
Late one but hopefully useful.
There is a free search tool from ApexSQL that can find and rename objects in database.
They say it has a smart rename option that will find/replace all occurrences of some object such as table, function or stored procedure.
I have to add that I haven’t used the rename functionality but I can confirm that search is working quite well.
Also I’m not affiliated with ApexSQL but I do use their tools.
To search: if you need to find database objects (e.g. tables, columns, triggers) by name - have a look at the FREE Red-Gate tool called SQL Search which does this - it searches your entire database for any kind of string(s).
It's a great must-have tool for any DBA or database developer - did I already mention it's absolutely FREE to use for any kind of use?
This tool doesn't support replacing text, however - but even just being able to find all the relevant stored procedures (or other DB objects) is very helpful indeed!
Export all SPs to file. Use your favourite text editing tool to search/replace. Update database by executing the script (as long as you do not rename procedures).
If you explicitly define the full database path, you need to manually (see above) update the stored procedures. If you do not include the database name, or use a linked server or similar, no changes are necessary.
Stored procedures cannot be updated in place without first scripting them out as ALTER PROCEDURE statements (or DROP/CREATE, but I prefer ALTER PROCEDURE..more on that in a moment). The good news is, you can script all the procedures to a single file through SSMS. The DDL statements will initially be CREATE PROCEDURE, which you'll want to replace with ALTER PROCEDURE, along with your other changes.
While you could alternatively script the procedures as DROP/CREATE, I don't like doing this for a large number of scripts because it tends to cause dependency errors.
As for part 2 of your question, you'll need to edit any database path changes manually through the script.
I found this script where you can define search for and replace by text and simply run it to get text replaced in all procedures at once. I hope this will help you in bulk.
-- set "Result to Text" mode by pressing Ctrl+T
SET NOCOUNT ON
DECLARE #sqlToRun VARCHAR(1000), #searchFor VARCHAR(100), #replaceWith VARCHAR(100)
-- text to search for
SET #searchFor = '[MY-SERVER]'
-- text to replace with
SET #replaceWith = '[MY-SERVER2]'
-- this will hold stored procedures text
DECLARE #temp TABLE (spText VARCHAR(MAX))
DECLARE curHelp CURSOR FAST_FORWARD
FOR
-- get text of all stored procedures that contain search string
-- I am using custom escape character here since i need to espape [ and ] in search string
SELECT DISTINCT 'sp_helptext '''+OBJECT_SCHEMA_NAME(id)+'.'+OBJECT_NAME(id)+''' '
FROM syscomments WHERE TEXT LIKE '%' + REPLACE(REPLACE(#searchFor,']','\]'),'[','\[') + '%' ESCAPE '\'
ORDER BY 'sp_helptext '''+OBJECT_SCHEMA_NAME(id)+'.'+OBJECT_NAME(id)+''' '
OPEN curHelp
FETCH next FROM curHelp INTO #sqlToRun
WHILE ##FETCH_STATUS = 0
BEGIN
--insert stored procedure text into a temporary table
INSERT INTO #temp
EXEC (#sqlToRun)
-- add GO after each stored procedure
INSERT INTO #temp
VALUES ('GO')
FETCH next FROM curHelp INTO #sqlToRun
END
CLOSE curHelp
DEALLOCATE curHelp
-- find and replace search string in stored procedures
-- also replace CREATE PROCEDURE with ALTER PROCEDURE
UPDATE #temp
SET spText = REPLACE(REPLACE(spText,'CREATE PROCEDURE', 'ALTER PROCEDURE'),#searchFor,#replaceWith)
SELECT spText FROM #temp
-- now copy and paste result into new window
-- then make sure everything looks good and run
GO
Here is the reference link :
http://www.ideosity.com/ourblog/post/ideosphere-blog/2013/06/14/how-to-find-and-replace-text-in-all-stored-procedures
You can search the text of the stored procedure definitions using this
SELECT
Name
FROM
sys.procedures
WHERE
OBJECT_DEFINITION(OBJECT_ID) LIKE '%YourSearchText%'
Replacing is generally a bad idea, since you don't know the context of the text you'll find in the stored procedures. It probably is possible though via Powershell scripting.
I prefer this solution to any others, since I'm comfortable writing queries- so finding text in all stored procs, that are in schema (x) and database (y) and names that start with (z) is quite an easy and intuitive query.
Here's one I wrote today to help with a server upgrade project.
Searches all stored procs and views in all user databases on a server, and automatically replaces the search string with another. Ideal for changing hard-coded linked server names and the like:
set nocount on
if OBJECT_ID('tempdb..#dbs') is not null
drop table #dbs
if OBJECT_ID('tempdb..#objects') is not null
drop table #objects
declare #find as nvarchar(128) = 'Monkey'
declare #replace as nvarchar(128) = 'Chimp'
declare #SQL as nvarchar(max)
declare #current_db as sysname
declare #current_schema as sysname
declare #current_object as sysname
declare #current_type as char(2)
declare #current_ansi as bit
declare #current_quot as bit
declare #fullname as sysname
declare #preamble as nvarchar(128)
create table #objects
(
dbname sysname,
schemaname sysname,
objname sysname,
objtype char(2),
ansinulls bit,
quotedidentifier bit
)
create unique clustered index i on #objects (dbname, schemaname, objname)
select [name] into #dbs
from master.sys.databases
where [name] not in ('master','tempdb','model','msdb','ReportServer','ReportServerTempDB', 'SSISDB')
declare db_cursor cursor for select [name] from #dbs order by [name]
open db_cursor
fetch next from db_cursor into #current_db
while ##FETCH_STATUS = 0
begin
set #SQL = 'insert into #objects select ''' + #current_db + ''', s.[name], o.[name], o.[type], m.uses_ansi_nulls, m.uses_quoted_identifier from ' + #current_db + '.sys.sql_modules as m '
+ 'join ' + #current_db + '.sys.objects AS o ON m.object_id = o.object_id '
+ 'join ' + #current_db + '.sys.schemas AS s ON o.schema_id = s.schema_id '
+ 'where m.definition like ''%' + #find + '%'' and type in (''P'', ''V'') and is_ms_shipped = 0 order by s.[name], o.[name]'
exec sp_executeSQL #SQL
fetch next from db_cursor into #current_db
end
close db_cursor
deallocate db_cursor
declare obj_cursor cursor for select dbname, schemaname, objname, objtype, ansinulls, quotedidentifier from #objects order by dbname, objname
open obj_cursor
fetch next from obj_cursor into #current_db, #current_schema, #current_object, #current_type, #current_ansi, #current_quot
while ##FETCH_STATUS = 0
begin
set #fullname = #current_db + '.' + #current_schema + '.' + #current_object
set #preamble = CASE WHEN #current_ansi = 1 THEN 'SET ANSI_NULLS ON' ELSE 'SET ANSI_NULLS OFF' END + '; '
+ CASE WHEN #current_quot = 1 THEN 'SET QUOTED_IDENTIFIER ON' ELSE 'SET QUOTED_IDENTIFIER OFF' END + '; '
print 'Altering ' + #fullname
if #current_type = 'P'
begin
set #SQL = 'use ' + #current_db + '; ' + #preamble + 'declare #newproc nvarchar(max);'
+ 'set #newproc = REPLACE(REPLACE(OBJECT_DEFINITION(OBJECT_ID(''' + #fullname + ''')), ''' + #find + ''', ''' + #replace + '''), ''CREATE PROCEDURE'', ''ALTER PROCEDURE''); '
+ 'exec sp_executeSQL #newproc'
exec sp_executeSQL #SQL
end
if #current_type = 'V'
begin
set #SQL = 'use ' + #current_db + '; ' + #preamble + 'declare #newproc nvarchar(max);'
+ 'set #newproc = REPLACE(REPLACE(OBJECT_DEFINITION(OBJECT_ID(''' + #fullname + ''')), ''' + #find + ''', ''' + #replace + '''), ''CREATE VIEW'', ''ALTER VIEW''); '
+ 'exec sp_executeSQL #newproc'
exec sp_executeSQL #SQL
end
fetch next from obj_cursor into #current_db, #current_schema, #current_object, #current_type, #current_ansi, #current_quot
end
close obj_cursor
deallocate obj_cursor
It also handles idiosyncratic ANSI_NULL and QUOTED_IDENTIFIER settings, and can be extended to handle the various types of function.
Be careful though! With great power comes great responsibility...
Update
I just realized the link in David's answer included the search function. again, it's a great answer.
David Atkinson's answer is great, just want to add the search part. (not sure when the search was added in SSMS, my version is SSMS V17.9.1)
Instead of selecting stored procedures one by one, I can do a search.
The search takes a wildcard, similar to 'like' in TSQL
There's no way to do this with built-in functionality. While it doesn't help you today, I'd suggest changing all of your references to synonyms while you're in there. That way, when this happens again in the future (and it will happen again), all of your external references are in one place and easily updated. Incidentally, I have a blog post on the latter.
I just run this code to find a specific text in all stored procedures:
SELECT DISTINCT
o.name AS Object_Name,
o.type_desc
FROM sys.sql_modules m
INNER JOIN
sys.objects o
ON m.object_id = o.object_id
WHERE m.definition Like '%textToFind%'
or m.definition Like '%\[ifTextIsAColNameWithBrackets\]%' ESCAPE '\';
If you have downtime available.
Go into "Generate scripts" and generate 'create' scripts for all of your sprocs you want to edit.
Replace the text in the script and just drop and re-create all of them.
Hmm, dropping and rebuilding all procedures worked, unfortunately it crashed the SQL server upon which the SCADA for a rather large factory relied.
It saved a bit of effort editing them individually and the factory was only stalled til I rebooted the server.
But exercise some caution methinks. I was fair crapping myself for a moment there.
I am writing a cleanup script. This script will run on weekend and clean up the db. Tables are related to Eamils and path of attachments are being stored in table. In cleanup of tables I also have to delete files from folder.
The path of files is like following.
\\xxx.xxx.xxx.xxx\EmailAttachments\Some Confirmation for xyz Children Centre_9FW4ZE1C57324B70EC79WZ15FT9FA19E.pdf
I can delete multiple files like following.
xp_cmdshell 'del c:\xyz.txt, abc.txt'
BUT when I create a CSV from table using FOR XML PATH('') the string cut off at the end. There might be 1000s of rows to delete so I don't want to use cursor to delete files from folder.
How can I delete files from folder
without using cursor
What permissions do I need on
network folder to delete files using t-sql from sql server
EDIT:
I have used cursor and it looks ok, not taking so much time. One problem which I am facing is
The sql server consider file name with space as two files like following statement
xp_cmdshell 'del E:\Standard Invite.doc'
throws error
Could Not Find E:\Standard
Could Not Find C:\Windows\system32\Invite.doc
NULL
Thanks.
Personally, I wouldn't worry too much about using a cursor here. Cursors are only 'mostly evil'; as your task isn't a set-based operation a cursor may be the most effective solution.
Although you have a comment stating that it will take an "awful lot of time" to use a cursor, in this case the biggest overhead is the actual delete of the file (not the cursor).
Note: The file deletion is done by the Operation System, not by the RDBMS.
As the delete is being done by calling xp_cmdshell, and because it it a procedure (not a function, etc), you can't call it and pass in a table's contents.
What you could do is build up a string, and execute that. But note, you are limitted to a maximum of 8000 characters in this string. As you have already said that you may have thousands of files, you will certaily not fit it within 8000 characters.
This means that you are going to need a loop no matter what.
DECLARE
#command VARCHAR(8000),
#next_id INT,
#next_file VARCHAR(8000),
#total_len INT
SELECT
#command = 'DEL ',
#total_len = 4
SELECT TOP 1
#next_id = id,
#next_file = file_name + ', '
FROM
table_of_files_to_delete
ORDER BY
id DESC
WHILE (#next_file IS NOT NULL)
BEGIN
WHILE ((#total_len + LEN(#next_file)) <= 8000) AND (#next_file IS NOT NULL)
BEGIN
SELECT
#command = #command + #next_file,
#total_len = #total_len + LEN(#next_file)
SELECT
#next_file = NULL
SELECT TOP 1
#next_id = id,
#next_file = file_name + ', '
FROM
table_of_files_to_delete
WHERE
id < #next_id
ORDER BY
id DESC
END
SET #command = SUBSTRING(#command, 1, #total_len - 2) -- remove the last ', '
EXEC xp_cmdshell #command
SELECT
#command = 'DEL ',
#total_len = 4
END
Not pretty, huh?
What you may be able do, depending on what needs deleting, is to use wild-cards. For example:
EXEC xp_cmdshell 'DELETE C:\abc\def\*.txt'
To delete files with space in name you need to enclose the filename with "
xp_cmdshell 'del "E:\Standard Invite.doc"'
DECLARE #deleteSql varchar(500)
,#myPath varchar(500) = '\\DestinationFolder\'
SET #deleteSql = 'EXEC master..xp_cmdshell ''del '+#myPath +'*.csv'''
EXEC(#deleteSql)