ejecutar - ¿Cómo exportar datos como formato CSV desde SQL Server usando sqlcmd?
ejecutar bcp desde sql server (10)
¿No es esto para lo que fue diseñado el bcp
?
bcp "select col1, col2, col3 from database.schema.SomeTable" queryout "c:/MyData.txt" -c -t"," -r"/n" -S ServerName -T
Ejecute esto desde su línea de comando para verificar la sintaxis.
bcp /?
Por ejemplo:
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]
Tenga en cuenta que bcp
no puede dar salida a los encabezados de las columnas.
Ver: página de documentación de utilidad de bcp .
Ejemplo de la página de arriba:
bcp.exe MyTable out "D:/data.csv" -T -c -C 65001 -t , ...
Puedo fácilmente volcar datos en un archivo de texto como:
sqlcmd -S myServer -d myDB -E -Q "select col1, col2, col3 from SomeTable"
-o "MyData.txt"
Sin embargo, he consultado los archivos de ayuda para SQLCMD
pero no he visto una opción específica para CSV.
¿Hay alguna manera de volcar datos de una tabla en un archivo de texto CSV usando SQLCMD
?
Con PowerShell puede resolver el problema ordenadamente insertando Invoke-Sqlcmd en 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 incluye el módulo SqlServer , que contiene el cmdlet Invoke-Sqlcmd
, que tendrá incluso si solo instala SSMS 2016. Antes de eso, SQL Server 2012 incluía el antiguo módulo SQLPS , que cambiaría el directorio actual a SQLSERVER:/
cuando el módulo se usó por primera vez (entre otros errores) por lo que para ello, tendrá que cambiar la línea #Requires
arriba a:
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
Para adaptar el ejemplo de SQL Server 2008 y 2008 R2, elimine por completo la línea #Requires y use la utilidad sqlps.exe en lugar del host de PowerShell estándar.
Invoke-Sqlcmd es el equivalente de PowerShell de sqlcmd.exe. En lugar de texto, genera objetos System.Data.DataRow .
El parámetro -Query
funciona como el parámetro -Q
de sqlcmd.exe. Pase una consulta SQL que describa los datos que desea exportar.
El parámetro -Database
funciona como el parámetro -d
de sqlcmd.exe. Pase el nombre de la base de datos que contiene los datos que se exportarán.
El parámetro -Server
funciona como el parámetro -S
de sqlcmd.exe. Pase el nombre del servidor que contiene los datos que se exportarán.
Export-CSV es un cmdlet de PowerShell que serializa objetos genéricos a CSV. Se envía con PowerShell.
El parámetro -NoTypeInformation
suprime el resultado adicional que no forma parte del formato CSV. De forma predeterminada, el cmdlet escribe un encabezado con información de tipo. Te permite saber el tipo de objeto cuando lo deserializas más adelante con Import-Csv
, pero confunde herramientas que esperan CSV estándar.
El parámetro -Path
funciona como el parámetro -o
de sqlcmd.exe. Una ruta completa para este valor es más segura si está atrapado utilizando el antiguo módulo SQLPS .
El parámetro -Encoding
funciona como los parámetros -f
o -u
de sqlcmd.exe. De forma predeterminada, Export-Csv genera solo caracteres ASCII y reemplaza a todos los demás con signos de interrogación. Use UTF8 en su lugar para conservar todos los caracteres y ser compatible con la mayoría de las otras herramientas.
La principal ventaja de esta solución sobre sqlcmd.exe o bcp.exe es que no tiene que hackear el comando para generar un CSV válido. El cmdlet Export-Csv lo maneja todo por ti.
La principal desventaja es que Invoke-Sqlcmd
lee todo el conjunto de resultados antes de pasarlo a lo largo de la canalización. Asegúrese de tener suficiente memoria para todo el conjunto de resultados que desea exportar.
Es posible que no funcione sin problemas en miles de millones de filas. Si eso es un problema, puede probar las otras herramientas o lanzar su propia versión eficiente de Invoke-Sqlcmd
utilizando la clase System.Data.SqlClient.SqlDataReader .
Esta respuesta se basa en la solución de @ iain-elder, que funciona bien, excepto en el caso de la gran base de datos (como se señala en su solución). Toda la tabla debe caber en la memoria de su sistema, y para mí esta no era una opción. Sospecho que la mejor solución usaría System.Data.SqlClient.SqlDataReader y un serializador CSV personalizado ( ver aquí un ejemplo ) u otro lenguaje con un controlador MS SQL y serialización CSV. En el espíritu de la pregunta original que probablemente buscaba una solución sin dependencia, el siguiente código de PowerShell funcionó para mí. Es muy lento e ineficiente, especialmente al crear instancias de la matriz $ data y al llamar a Export-Csv en el modo de adición para cada línea $ chunk_size.
$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()
Opción alternativa con BCP:
exec master..xp_cmdshell ''BCP "sp_who" QUERYOUT C:/av/sp_who.txt -S MC0XENTC -T -c ''
Por sqlcmd
general, sqlcmd
viene con la utilidad bcp
(como parte de mssql-tools
) que exporta a CSV de forma predeterminada.
Uso:
bcp {dbtable | query} {in | out | queryout | format} datafile
Por ejemplo:
bcp.exe MyTable out data.csv
Para volcar todas las tablas en los archivos CSV correspondientes, aquí está el script 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
Puedes ejecutar algo como esto:
sqlcmd -S MyServer -d myDB -E -Q "select col1, col2, col3 from SomeTable"
-o "MyData.csv" -h-1 -s"," -w 700
-
-h-1
elimina encabezados de nombre de columna del resultado -
-s","
establece el separador de columna a, -
-w 700
establece el ancho de fila a 700 caracteres (esto tendrá que ser tan ancho como la fila más larga o se ajustará a la siguiente línea)
Puedes hacerlo de una manera hackish. Cuidado al usar el truco sqlcmd
. Si los datos tienen comillas dobles o comas, se encontrará con problemas.
Puede usar un script simple para hacerlo correctamente:
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'' 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!")
Una nota para cualquiera que quiera hacer esto pero que también tenga los encabezados de las columnas, esta es la solución que utilicé con un archivo por lotes:
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
Esto genera los resultados iniciales (incluidos los separadores ----, ---- entre los encabezados y los datos) en un archivo temporal, luego elimina esa línea filtrándola a través de findstr. Tenga en cuenta que no es perfecto porque está filtrando -,-
no funcionará si solo hay una columna en el resultado, y también filtrará las líneas legítimas que contienen esa cadena.
Una respuesta anterior casi me solucionó, pero no crea correctamente un CSV analizado.
Aquí está mi versión:
sqlcmd -S myurl.com -d MyAzureDB -E -s, -W -i mytsql.sql | findstr /V /C:"-" /B > parsed_correctly.csv
Alguien que dice que sqlcmd
está desactualizado a favor de alguna alternativa de PowerShell es olvidar que sqlcmd
no es solo para Windows. Estoy en Linux (y cuando en Windows evito PS de todos modos).
Habiendo dicho todo eso, encuentro bcp
más fácil.
sqlcmd -S myServer -d myDB -E -o "MyData.txt" ^
-Q "select bar from foo" ^
-W -w 999 -s","
La última línea contiene opciones específicas de CSV.
-
-W
eliminar espacios finales de cada campo individual -
-s","
establece el separador de columna en la coma (,) -
-w 999
establece el ancho de fila a 999 caracteres
La respuesta de scottm es muy similar a la que uso, pero creo que la -W
es una adición realmente agradable: no necesito recortar el espacio en blanco cuando consumo el CSV en otro lugar.
Consulte también la referencia sqlcmd de MSDN . Pone el /?
salida de la opción a la vergüenza.