¿Cómo exportar datos en formato CSV desde SQL Server usando sqlcmd?

136

Puedo volcar datos con bastante facilidad 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 examinado los archivos de ayuda SQLCMDpero 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?

Rayo
fuente
Debe ser a través de sqlcmd, o podría usar otro programa como el siguiente: codeproject.com/KB/aspnet/ImportExportCSV.aspx
Bernhard Hofmann
No tiene que ser así, pero quería saber con certeza si sqlcmd realmente podría hacer esto antes de sumergirme en alguna otra utilidad de exportación. Una cosa para mencionar es que debe ser programable.
Ray
Existe una herramienta complementaria de SSMS 2008 que genera resultados CSV de sus tablas que se pueden personalizar según el lugar y el orden mediante cláusulas. store.nmally.com/software/sql-server-management-studio-addons/…

Respuestas:

140

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 los encabezados de nombre de columna del resultado
  • -s"," establece el separador de columna en
  • -w 700 establece el ancho de fila en 700 caracteres (esto tendrá que ser tan ancho como la fila más larga o se ajustará a la siguiente línea)
scottm
fuente
22
La advertencia al hacerlo de esta manera es que sus datos pueden no contener comas.
Sarel Botha
1
@SarelBotha, puede solucionar ese problema '""' + col1 + '""' AS col1, envuelto en comillas dobles (duplicadas) o simplemente llamando a un procedimiento almacenado.
MisterIsaak
2
@JIsaak Luego, asegúrese de que sus datos no tengan comillas dobles o asegúrese de reemplazar sus comillas dobles con dos comillas dobles.
Sarel Botha
1
¿Podría alguien aclarar qué se debe hacer para permitir comas dentro de los datos? ¿Tenemos que rodear cada columna con '""' + ___ + '""'?
Ahmed
2
Esta respuesta ahora está desactualizada. Los scripts de PowerShell son más flexibles y se pueden ejecutar en SQL Server como un Agente de trabajo.
Clinton Ward,
75

Con PowerShell, puede resolver el problema de manera ordenada si conecta Invoke-Sqlcmd a 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 Invoke-Sqlcmdcmdlet, que tendrá incluso si instala SSMS 2016. Antes de eso, SQL Server 2012 incluía el antiguo módulo SQLPS , que cambiaría el directorio actual SQLSERVER:\cuando el módulo estaba instalado. utilizado por primera vez (entre otros errores), por lo que deberá cambiar la #Requireslínea anterior 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 para SQL Server 2008 y 2008 R2, elimine la #Requireslínea por completo y use la utilidad sqlps.exe en lugar del host estándar de PowerShell.

Invoke-Sqlcmd es el equivalente de PowerShell de sqlcmd.exe. En lugar de texto, genera objetos System.Data.DataRow .

El -Queryparámetro funciona como el -Qparámetro de sqlcmd.exe. Pase una consulta SQL que describa los datos que desea exportar.

El -Databaseparámetro funciona como el -dparámetro de sqlcmd.exe. Pásele el nombre de la base de datos que contiene los datos a exportar.

El -Serverparámetro funciona como el -Sparámetro de sqlcmd.exe. Pásele el nombre del servidor que contiene los datos a exportar.

Export-CSV es un cmdlet de PowerShell que serializa objetos genéricos en CSV. Se envía con PowerShell.

El -NoTypeInformationparámetro suprime la salida adicional que no forma parte del formato CSV. De forma predeterminada, el cmdlet escribe un encabezado con información de tipo. Le permite saber el tipo de objeto cuando lo deserializa más tarde Import-Csv, pero confunde las herramientas que esperan CSV estándar.

El -Pathparámetro funciona como el -oparámetro de sqlcmd.exe. Una ruta completa para este valor es más segura si está atascado con el antiguo módulo SQLPS .

El -Encodingparámetro funciona como los parámetros -fo -ude sqlcmd.exe. De forma predeterminada, Export-Csv genera solo caracteres ASCII y reemplaza todos los demás con signos de interrogación. Utilice UTF8 en su lugar para preservar todos los caracteres y mantenerse 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 CSV válido. El cmdlet Export-Csv lo maneja todo por usted.

La principal desventaja es que Invoke-Sqlcmdlee todo el conjunto de resultados antes de pasarlo por la tubería. Asegúrese de tener suficiente memoria para todo el conjunto de resultados que desea exportar.

Es posible que no funcione sin problemas para miles de millones de filas. Si eso es un problema, puede probar las otras herramientas o lanzar su propia versión eficiente del Invoke-Sqlcmduso de la clase System.Data.SqlClient.SqlDataReader .

Iain Samuel McLean Elder
fuente
55
Otras respuestas honestamente apestan, esta es la única forma de hacerlo correctamente. Sin embargo, desearía que fuera más obvio.
Shagglez
1
En SQL 2008 R2, tuve que ejecutar la herramienta "sqlps.exe" para usar Invoke-Sqlcmd. Aparentemente necesito SQL 2012 para usar Import-Module? De todos modos, funciona dentro de "sqlps.exe" - vea este hilo para más detalles .
Mister_Tom
1
@Mister_Tom buen punto. El módulo SQLPS se introdujo con SQL 2012. La respuesta ahora explica cómo adaptar el ejemplo para ediciones anteriores.
Iain Samuel McLean Élder
1
@JasonMatney PowerShell es la nueva interfaz administrativa para los sistemas Windows, pero se publicaron muchos consejos de SQL Server antes de que se convirtiera en estándar. ¡Difundir la palabra! :-)
Iain Samuel McLean Élder
1
Esta respuesta proporciona información útil y una forma ALTERNATIVA poderosa y flexible para manejar el problema, sin embargo, no responde completamente la pregunta original, ya que se hizo específicamente. También soy fanático de PowerShell, pero no dejemos que el evangelismo se convierta en histeria. SQLCMD no desaparecerá pronto.
barbacoa
69
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 a la coma (,)
  • -w 999   establece el ancho de fila en 999 caracteres

La respuesta de scottm está muy cerca de lo que uso, pero considero -Wque es una buena adición: no necesito recortar espacios en blanco cuando consumo el CSV en otro lugar.

Consulte también la referencia de MSDN sqlcmd . Pone /?en vergüenza la salida de la opción.

ESV
fuente
19
@sims "set nocount on" al comienzo de la consulta / inputfile
d -_- b
77
¿Cómo puedo eliminar el subrayado en los encabezados?
ntombela
@gugulethun: puede hacer una unión en su consulta para poner el nombre de la columna en la primera línea.
Nordes
2
Esto funciona a las mil maravillas, pero si su columna contiene el separador, obtengo un archivo csv dañado ...
Peter
Agregó este comentario a la respuesta aceptada también, pero ... puede solucionar ese problema '""' + col1 + '""' AS col1, envuelto en comillas dobles (duplicadas) o simplemente llamando a un procedimiento almacenado.
MisterIsaak
59

¿No bcpes para esto?

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 bcpno puede generar encabezados de columna.

Ver: página de documentos de la utilidad bcp .

Ejemplo de la página anterior:

bcp.exe MyTable out "D:\data.csv" -T -c -C 65001 -t , ...
johndacostaa
fuente
1
ServerName = YourcomputerName \ SQLServerName, solo entonces se ejecuta de otro modo error
Hammad Khan
1
En caso de que quiera ver la documentación completa de bcp.exe: technet.microsoft.com/en-us/library/ms162802.aspx
Robert Bernstein el
55
¿Qué hacer si necesita exportar los nombres de columna como encabezado también? ¿Existe una solución genérica sencilla con bcp?
Iain Samuel McLean Élder
@johndacosta muchas gracias. ¿Cómo imprimiría también los encabezados de columna? No veo un cambio fácil en ningún lado. ¡Gracias!
Rachael
1
bcpno incluye el encabezado (nombres de columna), pero es ~ 10 veces más rápido que sqlcmd(según mi experiencia). Para obtener datos realmente grandes, puede usar bcppara obtener los datos, y usar sqlcmd(seleccione el 0 superior * de ...) para obtener el encabezado, y luego combinarlos.
YJZ
12

Una nota para cualquiera que quiera hacer esto pero también tenga los encabezados de columna, esta es la solución que usé en 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 ya que se está filtrando: -,-no funcionará si solo hay una columna en la salida, y también filtrará las líneas legítimas que contienen esa cadena.

Rudismo
fuente
1
En su lugar, use el siguiente filtro: findtr / r / v ^ \ - [, \ -] * $> output.csv Por alguna razón, simle ^ [, \ -] * $ coincide con todas las líneas.
Vladimir Korolev
3
Siempre ponga una expresión regular con ^ dentro de comillas dobles, o obtendrá resultados extraños, porque ^ es un carácter de escape para cmd.exe. Ambas expresiones regulares anteriores no funcionan correctamente, por lo que puedo decir, pero esto sí: findtr / r / v "^ - [-,] * -. $" (El. Antes de $ parece ser necesario cuando se prueba con echo, pero puede que no sea para la salida sqlcmd)
JimG
También hay un problema con las fechas que tienen 2 espacios entre la fecha y la hora en lugar de uno. Cuando intenta abrir el CSV en Excel, se muestra como 00: 00.0. Una manera fácil de resolver esto sería buscar y reemplazar todo "" con "" en el lugar usando SED. El comando para agregar a su script sería: SED -i "s / / / g" output.csv. Más sobre SED gnuwin32.sourceforge.net/packages/sed.htm
PollusB
esta es la respuesta correcta, funciona perfectamente con la adición de @ JimG. Utilicé punto y coma para el separador, y un archivo sql para la entrada, el archivo contenía la parte noncount on y ansi_warning off, y el interruptor -W para la eliminación de espacio
robotik
1

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í esto no era una opción. Sospecho que la mejor solución sería usar System.Data.SqlClient.SqlDataReader y un serializador CSV personalizado ( consulte aquí para ver un ejemplo ) u otro idioma 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 para crear instancias de la matriz de datos $ y llamar a Export-Csv en modo de agregado 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()
jeffmax
fuente
1

Opción alternativa con BCP:

exec master..xp_cmdshell 'BCP "sp_who" QUERYOUT C:\av\sp_who.txt -S MC0XENTC -T -c '
arnav
fuente
1

Por lo general, sqlcmdviene con una bcputilidad (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
kenorb
fuente
0

Una respuesta anterior casi lo resolvió para mí, 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 sqlcmdestá desactualizado a favor de alguna alternativa de PowerShell está olvidando que sqlcmdno es solo para Windows. Estoy en Linux (y cuando estoy en Windows evito PS de todos modos).

Habiendo dicho todo eso, me resulta bcpmás fácil.

Hack-R
fuente
0

Desde las siguientes 2 razones, debe ejecutar mi solución en CMD:

  1. Puede haber comillas dobles en la consulta.
  2. El nombre de usuario y la contraseña de inicio de sesión a veces son necesarios para consultar una instancia remota de 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
Mohsen Abasi
fuente
-2

Puedes hacerlo de una manera hack. Cuidado con el sqlcmdtruco. 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!")

Fuente: Escritura de salida SQL a CSV con VBScript .

Sarel Botha
fuente
1
Una explicación para el voto negativo estaría bien. Mi respuesta es correcta: no puedes hacer esto con sqlcmd. También ofrecí una forma alternativa de realizar la tarea.
Sarel Botha el
44
El voto negativo es porque claramente PUEDES hacer lo que OP te pide con sqlcmd.
Brian Driscoll
2
@BrianDriscoll, no dijo que no se puede hacerlo sqlcmd, solo estábamos afirmando el hecho de que sqlcmdno se escapa correctamente de la coma, por lo que apenas se puede utilizar para cualquier salida CSV grave .
Sebastian
Dije eso, pero me cansé de los votos negativos, así que edité mi respuesta. Haré que mi edición sea más veraz.
Sarel Botha