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
Related
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!
In SQL, I need to return a set of records from one table based on each value of a field in a Lookup table. So I have a lookup table that contains a filename field and I need to iterate through each of the filenames and find the records in another table based on this field (filename is the keyfield in both tables). After I identify each set of records, I need to then save each of them as a CSV file locally on my computer. I tried the below cursor and it's not working. Can someone assist?
DECLARE #name VARCHAR(50) -- database name
DECLARE #path VARCHAR(256) -- path to save file
DECLARE #fileName VARCHAR(256) -- filename for output file DECLARE #fileDate VARCHAR(20) -- used for file name
SET #path = 'C:\Output\'
SELECT #fileDate = CONVERT(VARCHAR(20),GETDATE(),112)
DECLARE db_cursor CURSOR FOR
SELECT * FROM dbo.filenamelookup fl JOIN dbo.recordstable rt ON fl.filename = rt.filename OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #temp
WHILE ##FETCH_STATUS = 0
BEGIN
SET #fileName = #path + #name + '_' + #fileDate + '.csv'
BACKUP DATABASE #temp TO DISK = #fileName
FETCH NEXT FROM db_cursor INTO #name
END
CLOSE db_cursor
DEALLOCATE db_cursor
If you're using MSSQL, I would say use the BCP or SQLCMD utilities. Here is a previous post on the same issue:
https://dba.stackexchange.com/questions/23566/writing-select-result-to-a-csv-file
I am doing a bulk insert that I have to skip the last row. Otherwise , I got an error saying "Bulk Insert: Unexpected end-of-file (EOF) encountered in data file."
If I set ROWTERMINATOR='\r\n', then I got 0 rows imported.
I wonder if there is any code that can help me skip the lastrow of the txt file? (The last row is dynamic.) My company currently doesn't have SSIS installed.
My code for bulk insert is
Declare #SQL1 varchar(150), #path varchar(100),
#pathtable varchar(100), #date datetime
set #date = getdate()
-- set path for files
set #path= 'C:\imp\'
set #pathtable = #path + 'importfile.txt'
delete from IDX
-- set sql
set #SQL1 = "BULK INSERT dbo.table FROM '" + #pathtable
+ "' WITH (FIRSTROW = 2, MAXERRORS = 0)"
-- Bulk insert
exec(#sql1)
The issue is that the last row contains a row count from the export process. If you're able to modify the export process, make sure you use the SQL command:
SET NOCOUNT ON;
If you're using a GUI to export the data there should be a place to modify the T-SQL used or an option to set nocount on.
This will prevent the last row from writing out to your file.
If you cannot modify the export process... You can get crazy and right either a console application to read the data and remove the last line or a CLR that does basically that very task.. Open the file, remove the last line, save the file then call your stored procedure above to bulk insert your data.
You need to use single quotes ' multiple times, You have used double quotes " which are treated as identifiers in sql server.
Your query should look like this...
Declare #SQL1 varchar(150)
, #path varchar(100)
, #pathtable varchar(100)
, #date datetime
SET #date = getdate();
SET #path= 'C:\imp\'
SET #pathtable = #path + 'importfile.txt'
SET #SQL1 = 'BULK INSERT dbo.table
FROM ''' + #pathtable + '''
WITH (
FIRSTROW = 2
, MAXERRORS = 0
)';
Now if you print this SQL statement it would look like this...
PRINT #SQL1
RESULT:
BULK INSERT dbo.table
FROM 'C:\imp\importfile.txt'
WITH (
FIRSTROW = 2
, MAXERRORS = 0
)
I have some code like this that I use to do a BULK INSERT of a data file into a table, where the data file and table name are variables:
DECLARE #sql AS NVARCHAR(1000)
SET #sql = 'BULK INSERT ' + #tableName + ' FROM ''' + #filename + ''' WITH (CODEPAGE=''ACP'', FIELDTERMINATOR=''|'')'
EXEC (#sql)
The works fine for standard tables, but now I need to do the same sort of thing to load data into a temporary table (for example, #MyTable). But when I try this, I get the error:
Invalid Object Name: #MyTable
I think the problem is due to the fact that the BULK INSERT statement is constructed on the fly and then executed using EXEC, and that #MyTable is not accessible in the context of the EXEC call.
The reason that I need to construct the BULK INSERT statement like this is that I need to insert the filename into the statement, and this seems to be the only way to do that. So, it seems that I can either have a variable filename, or use a temporary table, but not both.
Is there another way of achieving this - perhaps by using OPENROWSET(BULK...)?
UPDATE:
OK, so what I'm hearing is that BULK INSERT & temporary tables are not going to work for me. Thanks for the suggestions, but moving more of my code into the dynamic SQL part is not practical in my case.
Having tried OPENROWSET(BULK...), it seems that that suffers from the same problem, i.e. it cannot deal with a variable filename, and I'd need to construct the SQL statement dynamically as before (and thus not be able to access the temp table).
So, that leaves me with only one option which is to use a non-temp table and achieve process isolation in a different way (by ensuring that only one process can be using the tables at any one time - I can think of several ways to do that).
It's annoying. It would have been much more convenient to do it the way I originally intended. Just one of those things that should be trivial, but ends up eating a whole day of your time...
You could always construct the #temp table in dynamic SQL. For example, right now I guess you have been trying:
CREATE TABLE #tmp(a INT, b INT, c INT);
DECLARE #sql NVARCHAR(1000);
SET #sql = N'BULK INSERT #tmp ...' + #variables;
EXEC master.sys.sp_executesql #sql;
SELECT * FROM #tmp;
This makes it tougher to maintain (readability) but gets by the scoping issue:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'CREATE TABLE #tmp(a INT, b INT, c INT);
BULK INSERT #tmp ...' + #variables + ';
SELECT * FROM #tmp;';
EXEC master.sys.sp_executesql #sql;
EDIT 2011-01-12
In light of how my almost 2-year old answer was suddenly deemed incomplete and unacceptable, by someone whose answer was also incomplete, how about:
CREATE TABLE #outer(a INT, b INT, c INT);
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'SET NOCOUNT ON;
CREATE TABLE #inner(a INT, b INT, c INT);
BULK INSERT #inner ...' + #variables + ';
SELECT * FROM #inner;';
INSERT #outer EXEC master.sys.sp_executesql #sql;
It is possible to do everything you want. Aaron's answer was not quite complete.
His approach is correct, up to creating the temporary table in the inner query. Then, you need to insert the results into a table in the outer query.
The following code snippet grabs the first line of a file and inserts it into the table #Lines:
declare #fieldsep char(1) = ',';
declare #recordsep char(1) = char(10);
declare #Lines table (
line varchar(8000)
);
declare #sql varchar(8000) = '
create table #tmp (
line varchar(8000)
);
bulk insert #tmp
from '''+#filename+'''
with (FirstRow = 1, FieldTerminator = '''+#fieldsep+''', RowTerminator = '''+#recordsep+''');
select * from #tmp';
insert into #Lines
exec(#sql);
select * from #lines
Sorry to dig up an old question but in case someone stumbles onto this thread and wants a quicker solution.
Bulk inserting a unknown width file with \n row terminators into a temp table that is created outside of the EXEC statement.
DECLARE #SQL VARCHAR(8000)
IF OBJECT_ID('TempDB..#BulkInsert') IS NOT NULL
BEGIN
DROP TABLE #BulkInsert
END
CREATE TABLE #BulkInsert
(
Line VARCHAR(MAX)
)
SET #SQL = 'BULK INSERT #BulkInser FROM ''##FILEPATH##'' WITH (ROWTERMINATOR = ''\n'')'
EXEC (#SQL)
SELECT * FROM #BulkInsert
Further support that dynamic SQL within an EXEC statement has access to temp tables outside of the EXEC statement. http://sqlfiddle.com/#!3/d41d8/19343
DECLARE #SQL VARCHAR(8000)
IF OBJECT_ID('TempDB..#BulkInsert') IS NOT NULL
BEGIN
DROP TABLE #BulkInsert
END
CREATE TABLE #BulkInsert
(
Line VARCHAR(MAX)
)
INSERT INTO #BulkInsert
(
Line
)
SELECT 1
UNION SELECT 2
UNION SELECT 3
SET #SQL = 'SELECT * FROM #BulkInsert'
EXEC (#SQL)
Further support, written for MSSQL2000 http://technet.microsoft.com/en-us/library/aa175921(v=sql.80).aspx
Example at the bottom of the link
DECLARE #cmd VARCHAR(1000), #ExecError INT
CREATE TABLE #ErrFile (ExecError INT)
SET #cmd = 'EXEC GetTableCount ' +
'''pubs.dbo.authors''' +
'INSERT #ErrFile VALUES(##ERROR)'
EXEC(#cmd)
SET #ExecError = (SELECT * FROM #ErrFile)
SELECT #ExecError AS '##ERROR'
http://msdn.microsoft.com/en-us/library/ms191503.aspx
i would advice to create table with unique name before bulk inserting.
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.