如何使用sqlcmd从SQL Server将数据导出为CSV格式?
我可以很容易地将数据转储到一个文本文件,如:
sqlcmd -S myServer -d myDB -E -Q "select col1, col2, col3 from SomeTable" -o "MyData.txt"
但是,我已经查看了SQLCMD
的帮助文件,但没有看到专门针对CSV的选项。
有没有办法使用SQLCMD
将表中的数据转储到CSV文本文件?
你可以运行这样的东西:
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个字符(这将需要与最长的行一样宽,否则将会换行到下一行)
sqlcmd -S myServer -d myDB -E -o "MyData.txt" ^ -Q "select bar from foo" ^ -W -w 999 -s","
最后一行包含CSV特定的选项。
-
-W
从每个单独的字段中删除尾随空格 -
-s","
将列分隔符设置为逗号(,) -
-w 999
将行宽设置为999个字符
斯科特姆的答案与我使用的非常接近,但是我发现-W
是一个非常好的补充:当我在其他地方使用CSV时,无需修剪空格。
另请参阅MSDN sqlcmd参考 。 它把/?
选项的输出羞耻。
这不是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
不能输出列标题。
使用PowerShell,您可以通过将Invoke-Sqlcmd滚动到Export-Csv中来整齐地解决问题。
#Requires -Module SqlServer Invoke-Sqlcmd -Query "SELECT * FROM DimDate;" ` -Database AdventureWorksDW2012 ` -Server localhost | Export-Csv -NoTypeInformation ` -Path "DimDate.csv" ` -Encoding UTF8
SQL Server 2016包含SqlServer模块,该模块包含Invoke-Sqlcmd
cmdlet,即使您只安装了SSMS 2016,也可以使用该模块 。在此之前,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
为了适应SQL Server 2008和2008 R2的示例,请完全删除#Requires
行,并使用sqlps.exe实用程序而不是标准的PowerShell主机。
Invoke-Sqlcmd是与sqlcmd.exe等效的PowerShell。 它将输出System.Data.DataRow对象而不是文本。
-Query
参数的工作方式与sqlcmd.exe的-Q
参数相同。 传递一个SQL查询来描述你想要导出的数据。
-Database
参数的工作方式与sqlcmd.exe的-d
参数相同。 将它传递给包含要导出的数据的数据库的名称。
-Server
参数的工作方式与sqlcmd.exe的-S
参数相同。 将它传递给包含要导出的数据的服务器的名称。
Export-CSV是一个将通用对象序列化为CSV的PowerShell cmdlet。 它附带PowerShell。
-NoTypeInformation
参数禁止不属于CSV格式的额外输出。 默认情况下,cmdlet使用types信息写入标题。 当您使用Import-Csv
反序列化对象时,它可以让您知道对象的types,但它会混淆期望标准CSV的工具。
-Path
参数的工作方式与sqlcmd.exe的-o
参数相同。 如果您使用旧的SQLPS模块,则此值的完整path是最安全的。
-Encoding
参数的工作方式与sqlcmd.exe的-f
或-u
参数相同。 默认情况下,Export-Csv只输出ASCII字符,并用问号replace所有其他字符。 使用UTF8来保留所有的字符,并保持兼容大多数其他工具。
此解决scheme与sqlcmd.exe或bcp.exe相比的主要优点是您无需破解命令即可输出有效的CSV。 Export-Csv cmdlet可以处理所有这些。
主要的缺点是, Invoke-Sqlcmd
在将其传递到pipe道之前读取整个结果集。 确保您有足够的内存来存储您要导出的整个结果集。
它可能无法顺利运行数十亿行。 如果这是一个问题,你可以尝试其他的工具,或者使用System.Data.SqlClient.SqlDataReader类来自定义有效版本的Invoke-Sqlcmd
。
对于任何想要做这件事的人来说,还有列标题,这是我使用batch file的解决scheme:
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将其过滤掉。 请注意,这是不完美的,因为它被过滤出来-,-
如果输出中只有一列,它将不起作用,它也会过滤掉包含该string的合法行。
BCP的备用选项:
exec master..xp_cmdshell 'BCP "sp_who" QUERYOUT C:\av\sp_who.txt -S MC0XENTC -T -c '
这个答案build立在@ iain-elder的解决scheme上,除了大型的数据库案例(正如他的解决scheme中指出的那样),这个解决scheme很好。 整个表格需要适合你的系统内存,对我来说这不是一个选项。 我怀疑最好的解决scheme将使用System.Data.SqlClient.SqlDataReader和一个自定义CSV序列化程序( 请参阅此处的示例 )或另一种语言与MS SQL驱动程序和CSV序列化。 在原来的问题,这可能正在寻找一个没有依赖解决scheme的精神下,下面的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()
你可以做一个黑客的方式。 小心使用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@me.here" ' 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 。