如何使用sqlcmd从SQL Server将数据导出为CSV格式?

How to export data as CSV format from SQL Server using sqlcmd?

提问人:Ray 提问时间:1/9/2009 最后编辑:SteveCRay 更新时间:3/24/2022 访问量:502804

问:

我可以很容易地将数据转储到文本文件中,例如:

sqlcmd -S myServer -d myDB -E -Q "select col1, col2, col3 from SomeTable" 
     -o "MyData.txt"

但是,我已经查看了帮助文件,但没有看到专门针对 CSV 的选项。SQLCMD

有没有办法使用表将数据转储到 CSV 文本文件中?SQLCMD

sql-server 文件 csv sqlcmd

评论

0赞 Bernhard Hofmann 1/9/2009
这必须通过 sqlcmd 进行,或者是否可以使用其他程序,如下所示: codeproject.com/KB/aspnet/ImportExportCSV.aspx
0赞 Ray 1/9/2009
不一定是,但我想确定 sqlcmd 是否真的可以做到这一点,然后再深入研究其他一些导出实用程序。值得一提的是,它确实需要是可编写脚本的。
0赞 7/14/2013
有一个 SSMS 2008 加载项工具,该工具可执行表的 CSV 输出,这些输出可以按 where 和 order by 子句进行自定义。store.nmally.com/software/sql-server-management-studio-addons/......

答:

-1赞 Sarel Botha 1/9/2009 #1

你可以用一种黑客的方式做到这一点。小心使用黑客。如果数据有双引号或逗号,您将遇到麻烦。sqlcmd

您可以使用一个简单的脚本来正确执行此操作:

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Data Exporter                                                 '
'                                                               '
' Description: Allows the output of data to CSV file from a SQL '
'       statement to either Oracle, SQL Server, or MySQL        '
' Author: C. Peter Chen, http://dev-notes.com                   '
' Version Tracker:                                              '
'       1.0   20080414 Original version                         '
'   1.1   20080807 Added email functionality                '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
option explicit
dim dbType, dbHost, dbName, dbUser, dbPass, outputFile, email, subj, body, smtp, smtpPort, sqlstr

'''''''''''''''''
' Configuration '
'''''''''''''''''
dbType = "oracle"                 ' Valid values: "oracle", "sqlserver", "mysql"
dbHost = "dbhost"                 ' Hostname of the database server
dbName = "dbname"                 ' Name of the database/SID
dbUser = "username"               ' Name of the user
dbPass = "password"               ' Password of the above-named user
outputFile = "c:\output.csv"      ' Path and file name of the output CSV file
email = "[email protected]"           ' Enter email here should you wish to email the CSV file (as attachment); if no email, leave it as empty string ""
  subj = "Email Subject"          ' The subject of your email; required only if you send the CSV over email
  body = "Put a message here!"    ' The body of your email; required only if you send the CSV over email
  smtp = "mail.server.com"        ' Name of your SMTP server; required only if you send the CSV over email
  smtpPort = 25                   ' SMTP port used by your server, usually 25; required only if you send the CSV over email
sqlStr = "select user from dual"  ' SQL statement you wish to execute
'''''''''''''''''''''
' End Configuration '
'''''''''''''''''''''



dim fso, conn

'Create filesystem object 
set fso = CreateObject("Scripting.FileSystemObject")

'Database connection info
set Conn = CreateObject("ADODB.connection")
Conn.ConnectionTimeout = 30
Conn.CommandTimeout = 30
if dbType = "oracle" then
    conn.open("Provider=MSDAORA.1;User ID=" & dbUser & ";Password=" & dbPass & ";Data Source=" & dbName & ";Persist Security Info=False")
elseif dbType = "sqlserver" then
    conn.open("Driver={SQL Server};Server=" & dbHost & ";Database=" & dbName & ";Uid=" & dbUser & ";Pwd=" & dbPass & ";")
elseif dbType = "mysql" then
    conn.open("DRIVER={MySQL ODBC 3.51 Driver}; SERVER=" & dbHost & ";PORT=3306;DATABASE=" & dbName & "; UID=" & dbUser & "; PASSWORD=" & dbPass & "; OPTION=3")
end if

' Subprocedure to generate data.  Two parameters:
'   1. fPath=where to create the file
'   2. sqlstr=the database query
sub MakeDataFile(fPath, sqlstr)
    dim a, showList, intcount
    set a = fso.createtextfile(fPath)

    set showList = conn.execute(sqlstr)
    for intcount = 0 to showList.fields.count -1
        if intcount <> showList.fields.count-1 then
            a.write """" & showList.fields(intcount).name & ""","
        else
            a.write """" & showList.fields(intcount).name & """"
        end if
    next
    a.writeline ""

    do while not showList.eof
        for intcount = 0 to showList.fields.count - 1
            if intcount <> showList.fields.count - 1 then
                a.write """" & showList.fields(intcount).value & ""","
            else
                a.write """" & showList.fields(intcount).value & """"
            end if
        next
        a.writeline ""
        showList.movenext
    loop
    showList.close
    set showList = nothing

    set a = nothing
end sub

' Call the subprocedure
call MakeDataFile(outputFile,sqlstr)

' Close
set fso = nothing
conn.close
set conn = nothing

if email <> "" then
    dim objMessage
    Set objMessage = CreateObject("CDO.Message")
    objMessage.Subject = "Test Email from vbs"
    objMessage.From = email
    objMessage.To = email
    objMessage.TextBody = "Please see attached file."
    objMessage.AddAttachment outputFile

    objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = smtp
    objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = smtpPort

objMessage.Configuration.Fields.Update

    objMessage.Send
end if

'You're all done!!  Enjoy the file created.
msgbox("Data Writer Done!")

来源:使用 VBScript 将 SQL 输出写入 CSV

评论

2赞 Sarel Botha 1/10/2012
对反对票的解释会很好。我的答案是正确的:你不能用sqlcmd做到这一点。我还提供了另一种完成任务的方法。
4赞 Brian Driscoll 4/27/2012
反对票是因为你显然可以用 sqlcmd 执行 OP 要求的操作。
2赞 Sebastian 9/5/2013
@BrianDriscoll,他并没有说不能用号来做,我们只是在陈述一个事实,即没有正确转义逗号,因此几乎无法用于任何严肃的 CSV 输出。sqlcmdsqlcmd
0赞 Sarel Botha 9/5/2013
我确实这么说过,但厌倦了反对票,所以我编辑了我的答案。我会让我的编辑更真实。
164赞 scottm 1/9/2009 #2

你可以运行如下操作:

sqlcmd -S MyServer -d myDB -E -Q "select col1, col2, col3 from SomeTable" 
       -o "MyData.csv" -h-1 -s"," -w 700
  • -h-1从结果中删除列名标题
  • -s","将列分隔符设置为 ,
  • -w 700将行宽设置为 700 个字符(这需要与最长行一样宽,否则它将换行到下一行)

评论

34赞 Sarel Botha 10/31/2012
这样做的注意事项是,您的数据可能不包含任何逗号。
2赞 MisterIsaak 12/12/2013
@SarelBotha,您可以使用 、 括在(加倍)双引号中或仅调用存储过程来解决该问题。'""' + col1 + '""' AS col1
3赞 Sarel Botha 12/13/2013
@JIsaak 然后确保您的数据没有任何双引号,或者确保将双引号替换为两个双引号。
3赞 Clinton Ward 5/24/2016
这个答案现在已经过时了。PowerShell 脚本更加灵活,可以作为作业代理在 SQL Server 中运行。
5赞 Jthorpe 8/9/2017
投了反对票,因为这不会生成有效的 csv 格式的文件。请参阅使用 .Export-csv
83赞 ESV 3/12/2010 #3
sqlcmd -S myServer -d myDB -E -o "MyData.txt" ^
    -Q "select bar from foo" ^
    -W -w 999 -s","

最后一行包含特定于 CSV 的选项。

  • -W从每个单独的字段中删除尾随空格
  • -s","将列分隔符设置为逗号 (,)
  • -w 999将行宽设置为 999 个字符

scottm 的答案与我使用的答案非常接近,但我发现这是一个非常好的补充:当我在其他地方使用 CSV 时,我不需要修剪空格。-W

另请参阅 MSDN sqlcmd 参考。它使选项的输出感到羞耻。/?

评论

20赞 d-_-b 3/31/2010
@sims query/inputfile 开头的“set nocount on”
9赞 ntombela 5/26/2010
如何删除标题上的下划线?
0赞 Nordes 4/20/2011
@gugulethun :您可以在查询中执行联合,以将列名放在第一行。
2赞 Peter 4/12/2013
这就像一个魅力,但如果你的列包含分隔符,我会得到一个损坏的 csv 文件......
0赞 MisterIsaak 12/12/2013
将此评论也添加到已接受的答案中,但是......您可以使用 、 括在(双)双引号中或仅调用存储过程来解决该问题。'""' + col1 + '""' AS col1
67赞 john.da.costa 3/25/2011 #4

这不是故意的吗?bcp

bcp "select col1, col2, col3 from database.schema.SomeTable" queryout  "c:\MyData.txt"  -c -t"," -r"\n" -S ServerName -T

从命令行运行此命令以检查语法。

bcp /?

例如:

usage: bcp {dbtable | query} {in | out | queryout | format} datafile
  [-m maxerrors]            [-f formatfile]          [-e errfile]
  [-F firstrow]             [-L lastrow]             [-b batchsize]
  [-n native type]          [-c character type]      [-w wide character type]
  [-N keep non-text native] [-V file format version] [-q quoted identifier]
  [-C code page specifier]  [-t field terminator]    [-r row terminator]
  [-i inputfile]            [-o outfile]             [-a packetsize]
  [-S server name]          [-U username]            [-P password]
  [-T trusted connection]   [-v version]             [-R regional enable]
  [-k keep null values]     [-E keep identity values]
  [-h "load hints"]         [-x generate xml format file]
  [-d database name]

请注意,不能输出列标题。bcp

请参阅:bcp 实用工具文档页。

上页示例:

bcp.exe MyTable out "D:\data.csv" -T -c -C 65001 -t , ...

评论

1赞 TheTechGuy 10/18/2011
ServerName = YourcomputerName\SQLServerName,否则才会执行错误
1赞 Robert Bernstein 11/12/2013
如果要查看 bcp.exe 的完整文档,请执行以下操作: technet.microsoft.com/en-us/library/ms162802.aspx
5赞 Iain Samuel McLean Elder 6/1/2014
如果您还需要将列名导出为标题,该怎么办?是否有使用 bcp 的简单通用解决方案?
0赞 Rachael 8/18/2015
@johndacosta非常感谢。您将如何打印列标题?我在任何地方都没有看到轻松切换到它。谢谢!
2赞 YJZ 6/5/2018
bcp不包括标题(列名),但它比(根据我的经验)快 ~10 倍。对于真正的大数据,你可以用它来获取数据,并使用(select top 0 * from ...)来获取标题,然后将它们组合起来。sqlcmdbcpsqlcmd
13赞 Rudism 9/1/2011 #5

对于任何希望执行此操作但也有列标题的人,请注意,这是我使用批处理文件的解决方案:

sqlcmd -S servername -U username -P password -d database -Q "set nocount on; set ansi_warnings off; sql query here;" -o output.tmp -s "," -W
type output.tmp | findstr /V \-\,\- > output.csv
del output.tmp

这会将初始结果(包括标头和数据之间的----,----分隔符)输出到临时文件中,然后通过 findstr 将其过滤掉,从而删除该行。请注意,它并不完美,因为它正在过滤掉 - 如果输出中只有一列,它将不起作用,并且它还会过滤掉包含该字符串的合法行。-,-

评论

1赞 Vladimir Korolev 9/8/2011
请改用以下筛选器:findstr /r /v ^\-[,\-]*$ >输出.csv 出于某种原因,simle ^[,\-]*$ 匹配所有行。
4赞 JimG 3/9/2012
始终在双引号内放置带有 ^ 的正则表达式,否则你会得到奇怪的结果,因为 ^ 是 cmd.exe 的转义字符。据我所知,上面的两个正则表达式都无法正常工作,但确实如此:findstr /r /v “^-[-,]*-.$”(使用 echo 测试时似乎需要 $ 之前的 . ,但可能不适用于 sqlcmd 输出)
0赞 PollusB 6/14/2012
日期和时间之间有 2 个空格而不是一个空格也存在问题。当您尝试在 Excel 中打开 CSV 时,它显示为 00:00.0。解决此问题的一种简单方法是使用 SED 就地搜索并替换所有 “ ” 替换为 “ ”。添加到脚本的命令为:SED -i “s/ / /g” output.csv。关于 SED gnuwin32.sourceforge.net/packages/sed.htm 的更多信息
0赞 robotik 11/18/2015
这是正确答案,与@JimG的加法完美配合。我使用分号作为分隔符,使用sql文件进行输入,该文件包含noncount on和ansi_warning off部分,以及用于删除空格的-W开关
112赞 7 revs, 3 users 81%Iain Samuel McLean Elder #6

使用 PowerShell,您可以通过管道连接到 来巧妙地解决问题。Invoke-SqlcmdExport-Csv

#Requires -Module SqlServer
Invoke-Sqlcmd -Query "SELECT * FROM DimDate;" `
              -Database AdventureWorksDW2012 `
              -Server localhost |
Export-Csv -NoTypeInformation `
           -Path "DimDate.csv" `
           -Encoding UTF8

Invoke-Sqlcmd 是 sqlcmd.exe 的 PowerShell 等效项。它输出的不是文本,而是 System.Data.DataRow 对象。

该参数的工作方式类似于 sqlcmd.exe 的参数。向其传递一个描述要导出的数据的 SQL 查询。-Query-Q

该参数的工作方式类似于 sqlcmd.exe 的参数。向其传递包含要导出的数据的数据库的名称。-Database-d

该参数的工作方式类似于 sqlcmd.exe 的参数。向其传递包含要导出的数据的服务器的名称。-Server-S

Export-CSV 是一个 PowerShell cmdlet,用于将泛型对象序列化为 CSV。它随 PowerShell 一起提供。

该参数禁止显示不属于 CSV 格式的额外输出。默认情况下,cmdlet 会写入包含类型信息的标头。当您稍后使用 时,它可以让您知道对象的类型,但它会混淆需要标准 CSV 的工具。-NoTypeInformationImport-Csv

该参数的工作方式类似于 sqlcmd.exe 的参数。-Path-o

该参数的工作方式类似于 sqlcmd.exe 的 or 参数。默认情况下,Export-Csv 仅输出 ASCII 字符,并用问号替换所有其他字符。请改用 UTF8 来保留所有字符并与大多数其他工具保持兼容。-Encoding-f-u

与 sqlcmd.exe 或 bcp.exe 相比,此解决方案的主要优点是无需破解命令即可输出有效的 CSV。Export-Csv cmdlet 会为您处理所有操作。

主要缺点是,在沿管道传递结果集之前,会先读取整个结果集。确保有足够的内存来存储要导出的整个结果集。Invoke-Sqlcmd

对于数十亿行,它可能无法顺利运行。如果这是一个问题,您可以尝试其他工具,或者使用 System.Data.SqlClient.SqlDataReader 类推出自己的高效版本。Invoke-Sqlcmd

SQL Server 版本之间的差异

从 SQL Server 2016 开始,作为 SqlServer 模块的一部分提供。Invoke-Sqlcmd

SQL Server 2012 具有旧的 SQLPS 模块。导入模块时,它会将当前位置更改为 。因此,您需要将上面的行更改为:SQLSERVER:\#Requires

Push-Location $PWD
Import-Module -Name SQLPS
# dummy query to catch initial surprise directory change
Invoke-Sqlcmd -Query "SELECT 1" `
              -Database  AdventureWorksDW2012 `
              -Server localhost |Out-Null
Pop-Location
# actual Invoke-Sqlcmd |Export-Csv pipeline

如果使用旧的 SQLPS 模块遇到困难,则 参数的完整路径是最安全的。Export-Csv-Path

若要使示例适用于 SQL Server 2008 和 2008 R2,请完全删除该行,并使用 sqlps.exe 实用工具而不是标准 PowerShell 主机。#Requires

评论

8赞 Shagglez 7/26/2014
老实说,其他答案很糟糕,这是正确操作的唯一方法。我希望它更明显。
1赞 Mister_Tom 2/4/2015
在 SQL 2008 R2 上,我必须运行“sqlps.exe”工具才能使用 Invoke-Sqlcmd。显然我需要 SQL 2012 才能使用 Import-Module?安利 它在“sqlps.exe”中工作 - 有关详细信息,请参阅此线程
1赞 Iain Samuel McLean Elder 2/4/2015
@Mister_Tom好点。SQLPS 模块是在 SQL 2012 中引入的。答案现在解释了如何使示例适用于旧版本。
1赞 Iain Samuel McLean Elder 7/3/2015
@JasonMatney PowerShell 是 Windows 系统的新管理界面,但在它成为标准之前,已经发布了许多 SQL Server 建议。广为流传!:-)
3赞 barbecue 9/5/2016
这个答案提供了有用的信息和一种强大而灵活的替代方法来处理这个问题,但是它确实完全没有回答原来的问题,因为它被具体问到。我也是powershell的粉丝,但我们不要让传福音变成歇斯底里。SQLCMD 不会很快消失。
1赞 Dinesh vishe 11/16/2016 #7

BCP 的替代选项:

exec master..xp_cmdshell 'BCP "sp_who" QUERYOUT C:\av\sp_who.txt -S MC0XENTC -T -c '
2赞 jeffmax 6/28/2017 #8

这个答案建立在 @iain-elder 的解决方案之上,除了大型数据库案例(正如他的解决方案中指出的那样),该解决方案运行良好。整个表格需要适合您的系统内存,对我来说这不是一个选项。我怀疑最好的解决方案是使用 System.Data.SqlClient.SqlDataReader 和自定义 CSV 序列化程序(有关示例,请参阅此处)或其他带有 MS SQL 驱动程序和 CSV 序列化的语言。本着可能正在寻找无依赖解决方案的原始问题的精神,下面的 PowerShell 代码对我有用。它非常缓慢且效率低下,尤其是在实例化 $data 数组和以追加模式为每个 $chunk_size 行调用 Export-Csv 时。

$chunk_size = 10000
$command = New-Object System.Data.SqlClient.SqlCommand
$command.CommandText = "SELECT * FROM <TABLENAME>"
$command.Connection = $connection
$connection.open()
$reader = $command.ExecuteReader()

$read = $TRUE
while($read){
    $counter=0
    $DataTable = New-Object System.Data.DataTable
    $first=$TRUE;
    try {
        while($read = $reader.Read()){

            $count = $reader.FieldCount
            if ($first){
                for($i=0; $i -lt $count; $i++){
                    $col = New-Object System.Data.DataColumn $reader.GetName($i)
                    $DataTable.Columns.Add($col)
                }
                $first=$FALSE;
            }

            # Better way to do this?
            $data=@()
            $emptyObj = New-Object System.Object
            for($i=1; $i -le $count; $i++){
                $data +=  $emptyObj
            }

            $reader.GetValues($data) | out-null
            $DataRow = $DataTable.NewRow()
            $DataRow.ItemArray = $data
            $DataTable.Rows.Add($DataRow)
            $counter += 1
            if ($counter -eq $chunk_size){
                break
            }
        }
        $DataTable | Export-Csv "output.csv" -NoTypeInformation -Append
    }catch{
        $ErrorMessage = $_.Exception.Message
        Write-Output $ErrorMessage
        $read=$FALSE
        $connection.Close()
        exit
    }
}
$connection.close()
1赞 kenorb 2/26/2018 #9

通常带有 bcp 实用程序(作为 的一部分),默认情况下导出为 CSV。sqlcmdmssql-tools

用法:

bcp {dbtable | query} {in | out | queryout | format} datafile

例如:

bcp.exe MyTable out data.csv

要将所有表转储到相应的 CSV 文件中,下面是 Bash 脚本:

#!/usr/bin/env bash
# Script to dump all tables from SQL Server into CSV files via bcp.
# @file: bcp-dump.sh
server="sql.example.com" # Change this.
user="USER" # Change this.
pass="PASS" # Change this.
dbname="DBNAME" # Change this.
creds="-S '$server' -U '$user' -P '$pass' -d '$dbname'"
sqlcmd $creds -Q 'SELECT * FROM sysobjects sobjects' > objects.lst
sqlcmd $creds -Q 'SELECT * FROM information_schema.routines' > routines.lst
sqlcmd $creds -Q 'sp_tables' | tail -n +3 | head -n -2 > sp_tables.lst
sqlcmd $creds -Q 'SELECT name FROM sysobjects sobjects WHERE xtype = "U"' | tail -n +3 | head -n -2 > tables.lst

for table in $(<tables.lst); do
  sqlcmd $creds -Q "exec sp_columns $table" > $table.desc && \
  bcp $table out $table.csv -S $server -U $user -P $pass -d $dbname -c
done

评论

4赞 David Ching 1/8/2021
BCP 允许指定字段和行分隔符,但如果这些分隔符出现在数据中,则不会对其进行转义。因此,在创建 CSV 文件方面,它并不比 SQLCmd 好。
0赞 Hack-R 3/30/2018 #10

上面的答案几乎为我解决了这个问题,但它没有正确创建解析的 CSV。

这是我的版本:

sqlcmd -S myurl.com -d MyAzureDB -E -s, -W -i mytsql.sql | findstr /V /C:"-" /B > parsed_correctly.csv

有人说它已经过时了,而支持一些 PowerShell 替代方案,却忘记了这不仅适用于 Windows。我在 Linux 上(在 Windows 上时,无论如何我都避免使用 PS)。sqlcmdsqlcmd

说了这么多,我确实觉得更容易。bcp

0赞 Mohsen Abasi 12/20/2018 #11

由于以下 2 个原因,您应该在 CMD 中运行我的解决方案:

  1. 查询中可能有双引号
  2. 有时需要登录用户名和密码来查询远程 SQL Server 实例

    sqlcmd -U [your_User]  -P[your_password] -S [your_remote_Server] -d [your_databasename]  -i "query.txt" -o "output.csv" -s"," -w 700
    
0赞 Shadi 2/18/2022 #12

尝试使用 python 包将逗号分隔的输出后处理为有效的 csv。sqlcmd-csv

https://github.com/shadiakiki1986/sqlcmd-csv

sqlcmd ... -s, ...
pip install git+https://github.com/shadiakiki1986/sqlcmd-csv.git
sqlcmd_csv out.txt out.csv