I am trying to insert data from multiple CSV files from a single folder to a single table.
I can do bulinsert for 1 file using the following code:
USE [dbname]
GO
BULK INSERT tablename
FROM 'path to csv files'
WITH
(
FIRSTROW = 2, -- as 1st one is header
FIELDTERMINATOR = ',', --CSV field delimiter
ROWTERMINATOR = '\n', --Use to shift the control to next row
TABLOCK
)
GO
The first thing I would say is I personally wouldn't do this in T-SQL, I'd use something a bit better suited to the task, the SSIS Multiple flat file connection manager for example. This is possible in T-SQL but it is not the most robust approach and has limitation (such as the directories that can even be accessed).
-- Set the directory to check for files
DECLARE #Directory NVARCHAR(200) = N'C:\Your File Location\';
-- Create a table variable to store the files output from xp_dirtree
DECLARE #Files TABLE (FileName NVARCHAR(255), Depth INT, IsFile BIT);
-- Get the files from the folder
INSERT #Files(FileName, Depth, IsFile)
EXECUTE master..xp_dirtree #Directory, 1, 1;
--select the filenames and use STRING_AGG to combine into a single string to execute
DECLARE #SQL NVARCHAR(MAX) =
( SELECT STRING_AGG(CONCAT('BULK INSERT YourTableName FROM ''',
CONCAT(#Directory, f.FileName), '''
WITH (
FIELDTERMINATOR = '','',
ROWTERMINATOR = ''\n'',
FIRSTROW = 1
)'), ';' + CHAR(10))
FROM #Files AS f
WHERE f.IsFile = 1
AND f.FileName LIKE '%.csv'-- Optionally limit files
);
PRINT #SQL;
-- Uncomment below line once you're happy with the SQL Printed
--EXECUTE sp_executesql #SQL;
Or if you want a bit more control over error handling, you could use a cursor to iterate the files:
-- Set the directory to check for files
DECLARE #Directory NVARCHAR(200) = N'C:\Import Location\';
-- Create a table variable to store the files output from xp_dirtree
DECLARE #Files TABLE (FileName NVARCHAR(255), Depth INT, IsFile BIT);
-- Get the files from the folder
INSERT #Files(FileName, Depth, IsFile)
EXECUTE master..xp_dirtree #Directory, 1, 1;
--declare a cursor to loop through the files, filtered for csv
DECLARE FileCursor CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
SELECT FilePath = CONCAT(#Directory, f.FileName)
FROM #Files AS f
WHERE f.IsFile = 1
AND f.FileName LIKE '%.csv'; -- Optionally limit files
OPEN FileCursor;
DECLARE #FilePath NVARCHAR(255);
FETCH FROM FileCursor INTO #FilePath;
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #FilePath;
DECLARE #sql NVARCHAR(MAX) =
CONCAT('BULK INSERT YourTableName FROM ''', #FilePath, '''
WITH (
FIELDTERMINATOR = '','',
ROWTERMINATOR = ''\n'',
FIRSTROW = 1
)');
BEGIN TRY
EXECUTE sp_executesql #SQL;
END TRY
BEGIN CATCH
-- Do something to handle errors
END CATCH
FETCH NEXT FROM FileCursor INTO #FilePath;
END
CLOSE FileCursor;
DEALLOCATE FileCursor;
As I say though, SSIS or another dedicated ETL tool is a better choice.
I have a scenario that #PRIVILEGEID will have multiple value and #ROLEID will be same every time for those privilege Id.
So I have to insert the data into table.
Below is the Code For Procedure:-
ALTER PROCEDURE [dbo].[ADD_ROLE] (
#ROLENAME varchar(50),
#PRIVILEGEID int )
AS
BEGIN
DECLARE #QUERY varchar(1000);
DECLARE #ROLEID int;
SET #ROLEID = ( SELECT Max(ROLEID)
FROM ( SELECT
Max(CREATED_DATE) AS MAX_DATE,
ROLEID
FROM ROLE
WHERE ROLENAME = #ROLENAME
--(ROlename can be changed dynamically, take 'Manager' as example as of now.)
AND CREATED_DATE IS NOT NULL
GROUP BY ROLEID ) X );
--PRINT #ROLEID
SET #QUERY = 'INSERT INTO [ROLES_PRIVILEGES] (ROLEID,PRIVILEGEID) VALUES (''' + Cast(#ROLEID AS varchar) + ''',''' + Cast(#PRIVILEGEID AS varchar) + ''')';
EXECUTE ( #QUERY );
END;
Now #PRIVILEGEID will have a dynamic list of multiple values for the fixed #ROLEID and My issue is I can pass only one #PRIVILEGEID at one time.
Exec ADD_ROLE 'BACKOFFICE',#PRIVILEGEID #PRIVILEGEID=[2, 3, 4, 5, 6]
-- (How to pass multiple value for PRIVILEGEID )
I have tried While loop also but not sure how to implement it.
Please help.
Here is an example where I take a comma delimited string and dump into a table. This process works by converting it into xml and using xmlqry to parse. I like it because I feel its easier to read and looks less convoluted.
Let me know if it works for you.
USE yourDatabase
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[_parsedelimited]
(
#str varchar(max)
)
AS
BEGIN
if object_id('tempdb..#Emails') is not null drop table #Emails;
CREATE TABLE #Emails (Email varchar(2500));
Declare #x XML;
select #x = cast('<A>'+ replace(#str,',','</A><A>')+ '</A>' as xml);
INSERT INTO #Emails (Email)
select
t.value('.', 'varchar(50)') as inVal
from #x.nodes('/A') as x(t);
SELECT * FROM #Emails;
drop table #Emails;
END
GO
Consider the following:
declare #role varchar(20) = 'BACKOFFICE',
#PRIVILEGEID varchar(100) = '2,3,4,5,6';
select #role, *
from string_split(#PRIVILEGEID, ',');
Using that select, you should be able to insert it into whatever table you'd like.
I have this problem where bulk insert fails with different files sizes, specifically, the first file would contain 433 rows while the second file has 2 rows only. But when I insert the 2 rowed file by itself, there's no error.
I have this as my bulk insert statement
set #sql = 'BULK INSERT #temptable
FROM '''+#location+'''
WITH
(
FIELDTERMINATOR = '','',
ROWTERMINATOR = ''\n'',
MAXERRORS = 100,
TABLOCK,
FIRSTROW = 2
)'
The above statement is contained in a stored procedure.
For more information, I will put my stored procedure here.
USE [THERMOWAVE]
GO
/****** Object: StoredProcedure [dbo].[UploadOmronData] Script Date: 02/13/2017 13:48:54 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[UploadOmronData]
#location as nvarchar(max)
as
begin
declare #sql as nvarchar(max)
-- give #location data from VB.net
--declare #location as varchar(max)
--set #location = 'C:\Users\jsumgo\Documents\Visual Studio 2010\Projects\TestUploader\TestUploader\bin\Debug\12320161428420dataUpload.csv'
-- Joshua Magsino 11/28/2016
-- TSql to upload dynamic column data source
--before uploading, make sure that datasource table columns are named equal to database table column
--Check if data is to be stored as reference or could be destroyed
--Check database to store and columns that would be affected
--set configurations to enable Ad Hoc Distributed Queries
/*
sp_configure 'show advanced options', 1;
RECONFIGURE;
GO
sp_configure 'Ad Hoc Distributed Queries', 1;
RECONFIGURE;
GO
*/
-- configure SQL to execute Ad Hoc Queries using MSOFFICE 2010
/*
USE [master]
GO
EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'AllowInProcess', 1
GO
EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'DynamicParameters', 1
GO
*/
-- fill temptable with data
---------------------------------------------------------------------------------------------------------------
--drop table #temptable
--create blank temptable
create table #temptable
(
[Number] Varchar(20)
)
--declare #sql as varchar(max)
declare #cursor CURSOR
declare #colname as nvarchar(30)
SET #cursor = CURSOR FOR
(Select colname from dbo.sorting)
OPEN #cursor
FETCH NEXT
FROM #cursor INTO #colname
WHILE ##FETCH_STATUS = 0
BEGIN
-- add from column 2 to end
if #colname <> 'Number'
begin
set #sql = 'ALTER TABLE #temptable ADD ['+#colName+'] NVARCHAR(max) NULL'
EXEC sp_executesql #sql, N'#colname nvarchar(max)', #colname
end
FETCH NEXT
FROM #cursor INTO #colName
END
CLOSE #cursor
DEALLOCATE #cursor
---------------------------------------------------------------------------------------------------------------
--declare #location as varchar(max)
--set #location = 'D:\Omron Data\Data\sample\asdasdasdasdasd - Copy.csv'
--declare #sql as varchar(max)
--Insert Data from CSV file to Temptable
set #sql = 'BULK INSERT #temptable
FROM '''+#location+'''
WITH
(
FIELDTERMINATOR = '','',
ROWTERMINATOR = ''\n'',
MAXERRORS = 100,
TABLOCK,
FIRSTROW = 2
)'
EXEC sp_executesql #sql, N'#location as nvarchar(max)', #location
--Insert Statement
--If the location of the new column is in the last column of the CSV file, then direct bulk insert
--safe solution is to insert based on the column name of each table
--cursor is needed
DECLARE #DBcursor CURSOR
DECLARE #DBColname as varchar(30)
DECLARE #DBCOL as nvarchar(max)
--Get Table Names from dbo.sorting
--since all table names are inserted from dbo.sorting, get column names from dbo.sorting
SET #DBcursor = CURSOR FOR
(Select colname from dbo.sorting)
OPEN #DBcursor
FETCH NEXT
FROM #DBcursor INTO #DBColname
WHILE ##FETCH_STATUS = 0
BEGIN
--assemble columns
set #DBCOL = (isnull(#DBCOL,''))+'['+ #DBColname +'],'
FETCH NEXT
FROM #DBcursor INTO #DBColname
END
CLOSE #DBcursor
DEALLOCATE #DBcursor
--remove last (,) from columns
set #DBCOL =LEFT(#DBCOL, LEN(#DBCOL) - 1)
--insert data from temptable to DataUpload
set #sql = 'insert into DataToUpload ('+#DBCOL+') select '+#DBCOL+' from #tempTable'
EXEC sp_executesql #sql,N'#DBCOL NVARCHAR(max)', #DBCOL
--Put BatchID for DataUpload
--BatchId format,Batch + Number(000) + year + month + day
declare #number as nvarchar(max)
set #number = RIGHT('000' +CAST((select ISNULL(max(case when BatchID = null or BatchID = '' then '000' else right(SUBSTRING(BatchID,1,8),3)+ 1 end),'000') from DataToUpload)AS VARCHAR(3)),3)
set #sql = 'update DataToUpload set BatchID = ''Batch'+cast(#number as varchar(max))+cast(year(getdate()) as varchar(4))+''+cast(MONTH(getdate())as varchar(2))+''+cast(DAY(getdate())as varchar(2)) +''' where BatchID is null'
EXEC sp_executesql #sql,N'#number NVARCHAR(max)', #number
--select * from DataToUpload order by BAtchID,Number asc
--select * from dbo.Sorting
--select * from temptable
--delete from DataToUpload
--clear dbo.sorting
delete from dbo.Sorting
--remove temptable
drop table #temptable
DBCC FREEPROCCACHE WITH NO_INFOMSGS;
end
and then the command in a vb.net system that fires the stored procedure through here.
Sub UploadOmronData(ByVal location As String)
'SQL Stored Procedure -- Save data from CSV file to SQL
Dim sqlcom As New SqlClient.SqlCommand
Try
With sqlcom
conn.Open()
.Connection = conn
.CommandTimeout = 100
.CommandText = "dbo.UploadOmronData"
.CommandType = CommandType.StoredProcedure
.Parameters.AddWithValue("#location", location)
.ExecuteReader()
End With
Catch ex As Exception
MsgBox(ex.Message + " " + location)
GC.Collect()
sqlcom.Dispose()
frmMain.Close()
End Try
conn.Close()
sqlcom.Dispose()
GC.Collect()
End Sub
And the error message which I receive:
Bulk load data conversion error (truncation) for row 2, column 1 (Number).
Bulk load data conversion error (truncation) for row 3, column 1 (Number).
ADDED INFORMATION
Here is the table structure of dbo.DataToUpload
CREATE TABLE [dbo].[DataToUpload](
[Number] [int] NULL,
[Date&Time] [nvarchar](max) NULL,
[ms] [nvarchar](50) NULL,
[CH34] [nvarchar](50) NULL,
[CH35] [nvarchar](50) NULL,
[CH36] [nvarchar](50) NULL,
[CH37] [nvarchar](50) NULL,
[CH38] [nvarchar](50) NULL,
[CH39] [nvarchar](50) NULL,
[Alarm1-10] [nvarchar](50) NULL,
[Alarm11-20] [nvarchar](50) NULL,
[Alarm21-30] [nvarchar](50) NULL,
[Alarm31-40] [nvarchar](50) NULL,
[AlarmOut] [nvarchar](50) NULL,
[BatchID] [nvarchar](50) NULL,
[CH31] [nvarchar](20) NULL,
[CH32] [nvarchar](20) NULL,
[CH33] [nvarchar](20) NULL
) ON [PRIMARY]
and the rows that I'm trying to insert
Number,Date&Time,ms,CH34,CH35,CH36,CH37,CH38,CH39,Alarm1-10,Alarm11-20,Alarm21-30,Alarm31-40,AlarmOut
1,2016-08-08 16:23:16,000,+61.2,+64.0,+35.4,+94.4,+185.4,+151.2,LLLLLLLLLL,LLLLLLLLLL,LLLLLLLLLL,LLLLLLLLLL,LLLL
2,2016-08-08 16:23:26,000,+61.1,+64.4,+35.4,+94.3,+185.4,+151.2,LLLLLLLLLL,LLLLLLLLLL,LLLLLLLLLL,LLLLLLLLLL,LLLL
and If it mayhelp, the system I created is a background worker, since I have to run the code always.
I reckon the cause is that you're asking BULK INSERT to load 14 columns of data, but you're importing into #TempTable which doesn't have enough fields in it.
Your code creates a #tempTable table, and you use a CURSOR to add extra fields to it... but I reckon the end result isn't a table with 14 fields in it.
Before your BULK INSERT #temptable command gets run, how many fields are in the #temptable table ?
For example, if you just had one field in this table, when you ran the bulk-insert it would insist on trying to import the entire row into that one field, complete with all the commas, but, of course, it doesn't fit in a 20-character string.
CREATE TABLE #temptable
(
[Number] VARCHAR(20)
)
The solution... simply change your temporary table to have the correct number of columns, and the BULK INSERT will work fine:
CREATE TABLE #temptable
(
[Number] NVARCHAR(200),
[Datey] datetime,
[ms] NVARCHAR(200),
[CH34] NVARCHAR(200),
[CH35] NVARCHAR(200),
[CH36] NVARCHAR(200),
[CH37] NVARCHAR(200),
[CH38] NVARCHAR(200),
[CH39] NVARCHAR(200),
[Alarm110] NVARCHAR(200),
[Alarm1120] NVARCHAR(200),
[Alarm2130] NVARCHAR(200),
[Alarm3140] NVARCHAR(200),
[AlarmOut] NVARCHAR(200)
)
Good luck !
P.S. One piece of the puzzle which is missing.... What data do you have in your dbo.sorting table ? This will dictate which fields end up in your temporary table, but you don't explain how this table is populated, or what data it contains.
The attributes
[BatchID] [nvarchar](50) NULL,
[CH31] [nvarchar](20) NULL,
[CH32] [nvarchar](20) NULL,
[CH33] [nvarchar](20) NULL
are missing from your file. With BULK_INSERT the columns have to match perfectly. Fix the number of columns, and it should fix the error.
DECLARE #SQL_BULK VARCHAR(MAX)
SET #SQL_BULK = 'BULK INSERT [dbo].[Table]
FROM ''' + #BatchFileName + '''
WITH
(
FIRSTROW = 2,
FIELDTERMINATOR = ''\t'',
ROWTERMINATOR = ''0x0a''
)'
EXEC (#SQL_BULK)
I have this code to do a bulk load. Works fine, but I would like to have the #BatchFileName also in there as a column (each row contains the same value).
Is this possible during the bulk load? Or how can I add it later on in a separate function?
Thanks in advance
i use this script to iterate and bulkinsert files in directory:
create table #x (name varchar(200))
DECLARE #query varchar (1000),#conta int ,#query2 varchar (1000),#NOME varchar(50)
set #conta=1
set #query ='master.dbo.xp_cmdshell "dir '+'C:\directoryname'+'*.csv' +' /b"'
insert #x exec (#query)
delete from #x where name is NULL
select identity(int,1,1) as ID, name into #y from #x
drop table #x
WHILE #conta<221 --number of files
BEGIN
SELECT #NOME=name FROM #y WHERE ID=#conta
set #Query2 ='BULK INSERT [dbo].[tablename] FROM '+ '''C:\directoryname'+#NOME+'''
WITH (
FIELDTERMINATOR = '','',ROWTERMINATOR = ''0x0a'')'
SELECT #query2
exec (#Query2)
set #conta=#conta+1
END
drop table #y
I'm trying to create a stored procedure to import from CSV. Everything works if I have a hard coded file path, but I want to take a file path as a parameter. When I try SQL Sever Management Studio generates an error:
Incorrect syntax near '#filePath'.
(In fact, if I put anything but a pure string(eg. 'C:'+'/dir') it gives an error.)
This is a simplified version of my code:
Create procedure [importFile](#filePath varchar(Max))
AS
BEGIN
create table #Temp
(
row1 int,
row2 varchar(5),
row3 bit
)
BULK insert
#Temp
from #filePath
With(
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
)
...
END
Any explanation?
Use dynamic SQL to inject the file name variable into a string with the bulk insert statement and the use sp_executesqlto execute it. You might want to add some error checking to check that the path is valid and so on.
CREATE PROCEDURE [importFile] (#filePath VARCHAR(MAX))
AS
BEGIN
CREATE TABLE #Temp
(
row1 int,
row2 varchar(5),
row3 bit
)
DECLARE #SQL NVARCHAR(MAX) = ''
SET #SQL = N'
BULK INSERT #Temp
FROM ''' + #filePath + '''
WITH (
FIELDTERMINATOR = '','',
ROWTERMINATOR = ''\n''
)'
-- ...
EXEC sp_executesql #SQL
END
-- to run it:
EXEC importFile 'd:\test.csv'