22

Is it possible to generate a csv file from a stored procedure in SQL Server? I created my stored procedure and I want to stored some result as csv, does someone know how to achieve this?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Al Phaba
  • 6,545
  • 12
  • 51
  • 83
  • 3
    In addition to the answers below, I would consider if you really want to generate the csv file from the procedure or not. The first problem is permissions: the SQL Server service account will need permission to write to an output folder, which may cause issues. The second problem is that it's usually best to separate getting data from formatting/presenting it. Let the procedure generate the results, and then use an external script to generate the csv file. That way you can do many different things with the results from the procedure, it's not 'hard-coded' to generate a csv file. – Pondlife Apr 18 '13 at 16:36

7 Answers7

4

I think it is possible to use bcp command. I am also new to this command but I followed this link and it worked for me.

WowBow
  • 7,137
  • 17
  • 65
  • 103
3

This script exports rows from specified tables to the CSV format in the output window for any tables structure. Hope, the script will be helpful for you -

DECLARE 
      @TableName SYSNAME
    , @ObjectID INT

DECLARE [tables] CURSOR READ_ONLY FAST_FORWARD LOCAL FOR 
    SELECT 
          '[' + s.name + '].[' + t.name + ']'
        , t.[object_id]
    FROM (
        SELECT DISTINCT
              t.[schema_id]
            , t.[object_id]
            , t.name
        FROM sys.objects t WITH (NOWAIT)
        JOIN sys.partitions p WITH (NOWAIT) ON p.[object_id] = t.[object_id]
        WHERE p.[rows] > 0
            AND t.[type] = 'U'
    ) t
    JOIN sys.schemas s WITH (NOWAIT) ON t.[schema_id] = s.[schema_id]
    WHERE t.name IN ('<your table name>')

OPEN [tables]

FETCH NEXT FROM [tables] INTO 
      @TableName
    , @ObjectID

DECLARE 
      @SQLInsert NVARCHAR(MAX)
    , @SQLColumns NVARCHAR(MAX)
    , @SQLTinyColumns NVARCHAR(MAX)

WHILE @@FETCH_STATUS = 0 BEGIN

    SELECT 
          @SQLInsert = ''
        , @SQLColumns = ''
        , @SQLTinyColumns = ''

    ;WITH cols AS 
    (
        SELECT 
              c.name
            , datetype = t.name
            , c.column_id
        FROM sys.columns c WITH (NOWAIT)
        JOIN sys.types t WITH (NOWAIT) ON c.system_type_id = t.system_type_id AND c.user_type_id = t.user_type_id
        WHERE c.[object_id] = @ObjectID
            AND c.is_computed = 0
            AND t.name NOT IN ('xml', 'geography', 'geometry', 'hierarchyid')
    )
    SELECT 
          @SQLTinyColumns = STUFF((
            SELECT ', [' + c.name + ']'
            FROM cols c
            ORDER BY c.column_id
            FOR XML PATH, TYPE, ROOT).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
        , @SQLColumns = STUFF((SELECT CHAR(13) +
            CASE 
                WHEN c.datetype = 'uniqueidentifier' 
                    THEN ' + '';'' + ISNULL('''' + CAST([' + c.name + '] AS VARCHAR(MAX)) + '''', ''NULL'')' 
                WHEN c.datetype IN ('nvarchar', 'varchar', 'nchar', 'char', 'varbinary', 'binary') 
                    THEN ' + '';'' + ISNULL('''' + CAST(REPLACE([' + c.name + '], '''', '''''''') AS NVARCHAR(MAX)) + '''', ''NULL'')' 
                WHEN c.datetype = 'datetime'
                    THEN ' + '';'' + ISNULL('''' + CONVERT(VARCHAR, [' + c.name + '], 120) + '''', ''NULL'')' 
                ELSE 
                ' + '';'' + ISNULL(CAST([' + c.name + '] AS NVARCHAR(MAX)), ''NULL'')'
            END
            FROM cols c
            ORDER BY c.column_id
            FOR XML PATH, TYPE, ROOT).value('.', 'NVARCHAR(MAX)'), 1, 10, 'CHAR(13) + '''' +')

    DECLARE @SQL NVARCHAR(MAX) = '    
    SET NOCOUNT ON;
    DECLARE 
          @SQL NVARCHAR(MAX) = ''''
        , @x INT = 1
        , @count INT = (SELECT COUNT(1) FROM ' + @TableName + ')

    IF EXISTS(
        SELECT 1
        FROM tempdb.dbo.sysobjects
        WHERE ID = OBJECT_ID(''tempdb..#import'')
    )
        DROP TABLE #import;

    SELECT ' + @SQLTinyColumns + ', ''RowNumber'' = ROW_NUMBER() OVER (ORDER BY ' + @SQLTinyColumns + ')
    INTO #import
    FROM ' + @TableName + ' 

    WHILE @x < @count BEGIN

        SELECT @SQL = STUFF((
        SELECT ' + @SQLColumns + ' + ''''' + '
        FROM #import 
        WHERE RowNumber BETWEEN @x AND @x + 9
        FOR XML PATH, TYPE, ROOT).value(''.'', ''NVARCHAR(MAX)''), 1, 1, '''')

        PRINT(@SQL)

        SELECT @x = @x + 10

    END'

    EXEC sys.sp_executesql @SQL

    FETCH NEXT FROM [tables] INTO 
          @TableName
        , @ObjectID

END

CLOSE [tables]
DEALLOCATE [tables]

In the output window you'll get something like this (AdventureWorks.Person.Person):

1;EM;0;NULL;Ken;J;Sánchez;NULL;0;92C4279F-1207-48A3-8448-4636514EB7E2;2003-02-08 00:00:00
2;EM;0;NULL;Terri;Lee;Duffy;NULL;1;D8763459-8AA8-47CC-AFF7-C9079AF79033;2002-02-24 00:00:00
3;EM;0;NULL;Roberto;NULL;Tamburello;NULL;0;E1A2555E-0828-434B-A33B-6F38136A37DE;2001-12-05 00:00:00
4;EM;0;NULL;Rob;NULL;Walters;NULL;0;F2D7CE06-38B3-4357-805B-F4B6B71C01FF;2001-12-29 00:00:00
5;EM;0;Ms.;Gail;A;Erickson;NULL;0;F3A3F6B4-AE3B-430C-A754-9F2231BA6FEF;2002-01-30 00:00:00
6;EM;0;Mr.;Jossef;H;Goldberg;NULL;0;0DEA28FD-EFFE-482A-AFD3-B7E8F199D56F;2002-02-17 00:00:00
Devart
  • 119,203
  • 23
  • 166
  • 186
2

Found a really helpful link for that. Using SQLCMD for this is really easier than solving this with a stored procedure

http://www.excel-sql-server.com/sql-server-export-to-excel-using-bcp-sqlcmd-csv.htm

Al Phaba
  • 6,545
  • 12
  • 51
  • 83
2

You can do this using OPENROWSET as suggested in this answer. Reposting Slogmeister Extrarodinare answer:

Use T-SQL

INSERT INTO OPENROWSET ('Microsoft.ACE.OLEDB.12.0','Text;Database=D:\;HDR=YES;FMT=Delimited','SELECT * FROM [FileName.csv]')
SELECT Field1, Field2, Field3 FROM DatabaseName

But, there are couple of caveats:

  1. You need to have the Microsoft.ACE.OLEDB.12.0 provider available. The Jet 4.0 provider will work, too, but it's ancient, so I used this one instead.

  2. The .CSV file will have to exist already. If you're using headers (HDR=YES), make sure the first line of the .CSV file is a delimited list of all the fields.

jitin14
  • 144
  • 5
1

I have tried this and it is working fine for me:

sqlcmd -S servername -E -s~ -W -k1 -Q  "sql query here" > "\\file_path\file_name.csv"
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Maddy
  • 11
  • 1
0

I also used bcp and found a couple other helpful posts that would benefit others if finding this thread

Don't use VARCHAR(MAX) as your @sql or @cmd variable for xp_cmdshell; you will get and error

Msg 214, Level 16, State 201, Procedure xp_cmdshell, Line 1 Procedure expects parameter 'command_string' of type 'varchar'.

http://www.sqlservercentral.com/Forums/Topic1071530-338-1.aspx

Use NULLIF to get blanks for the csv file instead of a NUL (viewable in hex editor, or notepad++). I used that in the SELECT statement for bcp

How to make Microsoft BCP export empty string instead of a NUL char?

Community
  • 1
  • 1
0

Just to answer a native way to do this that finally worked, everything had to be casted as a varchar

ALTER PROCEDURE [clickpay].[sp_GetDocuments] 

@Submid bigint

AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
   DECLARE @raw_sql varchar(max)

DECLARE @columnHeader VARCHAR(max)
SELECT @columnHeader = COALESCE(@columnHeader+',' ,'')+ ''''+column_name +'''' FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Documents'

DECLARE @ColumnList VARCHAR(max)
SELECT @ColumnList = COALESCE(@ColumnList+',' ,'')+ 'CAST('+column_name +' AS VARCHAR)' FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Documents'

SELECT @raw_sql = 'SELECT '+ @columnHeader +' UNION ALL SELECT ' + @ColumnList + ' FROM ' + 'Documents where Submid = ' +  CAST(@Submid AS VARCHAR)

-- Insert statements for procedure here
exec(@raw_sql)
END
thothal
  • 16,690
  • 3
  • 36
  • 71
MichaelEvanchik
  • 1,748
  • 1
  • 13
  • 23
  • please put 4 spaces before "END" so that its included in the code block. Edits require 6 chars minimum so I wasn't able to do it for you. – Jeff Mergler Jan 17 '19 at 21:08