Click here to Skip to main content
15,891,033 members
Articles / Programming Languages / SQL

Import Data from a Text or CSV file into SQL Server

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
26 Nov 2011CPOL2 min read 437.5K   18   6
Import Data from a Text or CSV file into SQL Server

I was recently in need of importing data from a CSV (Comma Separated File) to a table in SQL Server 2008.
After searching in Google for a little while, I found this blog entry from Pinal Dave (SQL Authority) which always provides good content.

Besides passing along the technique described in Pinal's blog post, I would like to expand by explaining a few snags that I ran into.

My source data in the CSV file looks something like this:

HOU009,Windows Phone 7,Will Martinez,
11/10/2011,Houston; TX,999,2
HOU010,WPF for Business Applications,Will Martinez,
11/15/2011,Houston; TX,695,1

More or less, I have all the data needed to fill the columns of my target table. My assumption is that my column id of type uniqueidentifier is going to be auto-generated. This is my table:

SQL
CREATE TABLE [dbo].[Courses](
   [id] [uniqueidentifier] NOT NULL DEFAULT NEWSEQUENTIALID(),
   [code] [varchar](50) NOT NULL,
   [description] [varchar](200) NULL,
   [instructor] [varchar](50) NULL,
   [date] [date] NULL,
       [venue] [varchar](50) NULL,
   [price] [money] NULL,
       [duration] [int] NULL,
 CONSTRAINT [PK_Courses] 
    PRIMARY KEY CLUSTERED ([id] ASC,[code] ASC )
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, _
	IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
  ON [PRIMARY]) ON [PRIMARY]

As you may notice, I have two datatypes that may be challenging, first my primary key is of type uniqueidentifier and also, I'm using a date datatype.

Initially, I tried to import my data from the CSV file using the below statement:

SQL
bulk insert [dbo].[Courses]
from 'C:\Courses.csv'
with (fieldterminator = ',', rowterminator = '\n')
go

On my first try, I did not have any luck. I ran into the below issue:

Msg 4861, Level 16, State 1, Line 1
Cannot bulk load because the file "C:\Courses.csv" 
could not be opened. Operating system error code 5
(Access is denied.).

Moved my file to the Public directory and then made sure permissions were set to 'Everyone'. No luck, although I did not get the "access denied" error. My insert returns 0 row(s) affected.

At this point, my guess was that I had a couple of issues:

  • First, I did not have a value for the Id column which is our primary key
  • My column of type date could be a problem if there is an implicit conversion of string data

To work around the problem, I decided to create a new temporary table, without a primary key and with a datetime datafield. Here is the table:

SQL
CREATE TABLE [dbo].[CoursesTemp](
   [code] [varchar](50) NOT NULL,
   [description] [varchar](200) NULL,
   [instructor] [varchar](50) NULL,
   [date] [datetime] NULL,
   [venue] [varchar](50) NULL,
   [price] [money] NULL,
   [duration] [int] NULL)

Ran my insert statement again:

SQL
bulk insert [dbo].[CoursesTemp]
from 'C:\Users\Public\Downloads\Courses.csv'
with (fieldterminator = ',', rowterminator = '\n')
go

Success! Finally got to see what I was waiting for...(20 row(s) affected).

At this point, life is easy. I can use the data I just inserted in the temporary table and use it to insert into the "live" table.

SQL
insert [dbo].[Courses]
  (code, description, instructor, date, venue, duration)
select 
   code, description, instructor, cast(date as date), venue,
   duration
from [dbo].[CoursesTemp]

Notice that my Id column is not listed since it has a uniqueidentifier with a default of NEWSEQUENTIALID , it automatically generates a GUID for each record.

To deal with the issue of the date field, I noticed that there is a CAST statement that will convert the data to the appropriate datatype.

Below is how the data looks like in my table:

?

Now, I have the data I needed and my id column has nice GUIDs generated for every record.
I'm ready to get some work done.

Hope this helps somebody out there,
Will

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionIs there another way to do this, but without Bulk Insert? Pin
Boris Krepkanovich14-Feb-15 23:07
Boris Krepkanovich14-Feb-15 23:07 
QuestionCSV not supported by SQL Server Bulk import Pin
nanonerd18-Sep-13 10:34
nanonerd18-Sep-13 10:34 
QuestionQuestion about import txt file.. please help Pin
GaboBigale16-Jan-13 5:35
GaboBigale16-Jan-13 5:35 
AnswerRe: Question about import txt file.. please help Pin
gmtzgtz16-Jan-13 6:46
gmtzgtz16-Jan-13 6:46 
GeneralRe: Question about import txt file.. please help Pin
GaboBigale17-Jan-13 6:22
GaboBigale17-Jan-13 6:22 
Thanks for the advice to Pondlife, i got the answer for my own question.
Here we go;

Well as we know i have this text file that in the same time has the header and the detail

HR|001580/06|RG|11/01/2013 12:00|BG|3573|001580|
IT|001580/01|1|00147066||1200|852.3|830.3|1.35|UNIDAD|0|31/12/2014 00:00
IT|001580/02|1|00147066||200|852.3|830.3|1.35|UNIDAD|1|31/12/2014 00:00
IT|001580/03|1|00147066||100|852.3|830.3|1.35|UNIDAD|55|31/12/2014 00:00
IT|001580/04|2|00254276||200|852.3|830.3|1.35|UNIDAD|0|31/12/2014 00:00
IT|001580/05|3|00305359||1700|852.3|830.3|1.35|UNIDAD|0|31/12/2014 00:00
IT|001580/06|3|00305359||300|852.3|830.3|1.35|UNIDAD|1|31/12/2014 00:00

So i need to:

1. Read the file
2. Insert the header into table1
3. Insert the detail into table2

For the First Step i create a table and then i use the Sql BulkInsert

SQL
CREATE TABLE #ImportData
    (
    	Field VARCHAR(max)
    )
    -- Insert the data into the first table
    BULK INSERT #ImportData
    FROM 'PATH FILE'
    WITH 
    ( 
        ROWTERMINATOR = '\n'
    )

i got something like this.

----------
We see that the rows it has delimiters( | ), that means the fields of the each table (HR - Header, IT - Details)

Then for my second and third step i need insert the rows on each table, but also i need to separate each fields of each row, so i create a procedure that receive a string and delimiter and return a table with each field of my string, i created the fields dynamically.

If you want to create the procedure you need to run this first
SQL
SET QUOTED_IDENTIFIER OFF

Then you can create it.

SQL
--EXEC spSplit
    --	@InputString = 'IT|001580/01|1|00147066||1200|852.3|830.3|1.35|QUANTITY|0|31/12/2014 00:00', -- VARCHAR(max)
    --	@Delimiter = '|' -- VARCHAR(50)
    
    --SET QUOTED_IDENTIFIER OFF
    ALTER PROCEDURE spSplit
    -- =============================================
    -- Author:		Gabriel Jiménez
    -- Create date: 2013-01-16
    -- Description:	Procedimiento creado para retornar
    -- valores de cada campo de una cadena delimitada 
    -- =============================================
    	@InputString	NVARCHAR(max),
        @Delimiter		VARCHAR(50)
    
    AS
    BEGIN
    
    	SET QUOTED_IDENTIFIER OFF
    
    	CREATE TABLE #Items (Field1 VARCHAR(max))
    
    	DECLARE @Item			VARCHAR(max)
    	DECLARE @ItemList		VARCHAR(max)
    	DECLARE @DelimIndex		INT
    	DECLARE @PositionField	INT 
    	DECLARE @SqlTable		VARCHAR(max)
    	DECLARE @SqlUpdate		VARCHAR(max)
    
    	SET @ItemList = @InputString
    	SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
    	SET @PositionField = 0
    
    	WHILE (@DelimIndex != 0) 
    	BEGIN
    	
    		SET @SqlTable = "ALTER TABLE #Items ADD Field"
    		SET @SqlUpdate = "UPDATE #Items SET Field"
    		
    		-- Set positions Rows and Fields 
    		SET @PositionField = @PositionField + 1
    		SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex)
    		
    		IF @PositionField = 1
    		BEGIN
    			-- Insert the first data into the first field created on the table
    			INSERT INTO #Items (Field1) VALUES ( @Item )
    		END
    		ELSE
    		BEGIN
    			-- Create a dynamic table for the @InputString
    			SET @SqlTable = @SqlTable + CONVERT(VARCHAR, @PositionField)+ " VARCHAR(MAX)"
    			EXEC(@SqlTable)
    			
    			SET @SqlUpdate = @SqlUpdate + 
    				CONVERT(VARCHAR, @PositionField) + " = '" + 
    				CONVERT(VARCHAR, @Item) +"'"
    			EXEC(@SqlUpdate)
    			
    		END
    
    		-- Set @ItemList = @ItemList minus one less item
    		SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)-@DelimIndex)
    		SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
    		
    	END -- End WHILE
    	
    	IF @Item IS NOT NULL -- At least one delimiter was encountered in @InputString
    	BEGIN
    		SET @SqlTable = "ALTER TABLE #Items ADD Field"
    		SET @SqlUpdate = "UPDATE #Items SET Field"
    		SET @SqlTable = @SqlTable + CONVERT(VARCHAR, @PositionField+1)+ " VARCHAR(MAX)"
    		EXEC(@SqlTable)
    		
    		SET @SqlUpdate = @SqlUpdate + 
    			CONVERT(VARCHAR, @PositionField+1) + " = '" + 
    			CONVERT(VARCHAR, @ItemList) +"'"
    		EXEC(@SqlUpdate)
    		
    	END
    	-- No delimiters were encountered in @InputString, so just return @InputString
    	ELSE 
    	BEGIN
    		SET @SqlTable = "ALTER TABLE #Items ADD Field"
    		SET @SqlUpdate = "UPDATE #Items SET Field"
    		SET @SqlTable = @SqlTable + CONVERT(VARCHAR, @PositionField+1)+ " VARCHAR(MAX)"
    		EXEC(@SqlTable)
    		
    		SET @SqlUpdate = @SqlUpdate + 
    			CONVERT(VARCHAR, @PositionField+1) + " = '" + 
    			CONVERT(VARCHAR, @InputString) +"'"
    		EXEC(@SqlUpdate)
    		
    	END 
    
    	SELECT * FROM #Items
    	
    	SET QUOTED_IDENTIFIER ON 
    END -- End Procedure
    GO


If you see i comment the three first lines for execute the procedure and you can get something like this

Well we not finish yet, we read the file, and we create the procedure that return the fields and now we need insert into each table, for that i create two temp table with static fields 'cause i know the fields of each table and then create Cursor for read each row on my #ImporData table.

SQL
CREATE TABLE #Header
    (
    	Field1 VARCHAR(max),
    	Field2 VARCHAR(max),
    	Field3 VARCHAR(max),
    	Field4 VARCHAR(max),
    	Field5 VARCHAR(max),
    	Field6 VARCHAR(max),
    	Field7 VARCHAR(max),
    	Field8 VARCHAR(max)
    )
    -- Detail table is for the detail items 
    CREATE TABLE #Detail
    (
    	Field1 VARCHAR(max),
    	Field2 VARCHAR(max),
    	Field3 VARCHAR(max),
    	Field4 VARCHAR(max),
    	Field5 VARCHAR(max),
    	Field6 VARCHAR(max),
    	Field7 VARCHAR(max),
    	Field8 VARCHAR(max),
    	Field9 VARCHAR(max),
    	Field10 VARCHAR(max),
    	Field11 VARCHAR(max),
    	Field12 VARCHAR(max)
    )

    DECLARE @Field NVARCHAR(max)
    DECLARE Header CURSOR        
        FOR SELECT  Field
            FROM    #ImportData
            WHERE SUBSTRING(Field,1,2) = 'HR'
    OPEN Header
    FETCH NEXT FROM Header INTO @Field
    
    WHILE @@FETCH_STATUS = 0           
        BEGIN 
    		INSERT INTO #Header
    		EXEC spSplit @Field, '|'
    		FETCH NEXT FROM Header INTO @Field
        END                              
    CLOSE Header
    DEALLOCATE Header
    
    --Detail
    --DECLARE @f NVARCHAR(max)
    DECLARE Detail CURSOR        
        FOR SELECT  Field
            FROM    #ImportData
            WHERE SUBSTRING(Field,1,2) = 'IT'
    OPEN Detail
    FETCH NEXT FROM Detail INTO @Field
    
    WHILE @@FETCH_STATUS = 0           
        BEGIN 
    		INSERT INTO #Detail
    		EXEC spSplit @Field, '|'
    		FETCH NEXT FROM Detail INTO @Field
        END                              
    CLOSE Detail
    DEALLOCATE Detail

    --SELECT * FROM #ImportData
    SELECT * FROM #Header
    SELECT * FROM #Detail
    
    DROP TABLE #ImportData
    DROP TABLE #Header
    DROP TABLE #Detail



After that if you put all the pieces on the same query you got this.

As i need to.

----------
General:) Pin
Hector Perales26-Apr-12 20:05
Hector Perales26-Apr-12 20:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.