Come esportare i dati in formato CSV da SQL Server utilizzando sqlcmd?


136

Posso facilmente scaricare i dati in un file di testo come:

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

Tuttavia, ho esaminato i file della guida per SQLCMDma non ho visto un'opzione specifica per CSV.

Esiste un modo per scaricare i dati da una tabella in un file di testo CSV utilizzando SQLCMD?


Deve essere tramite sqlcmd o è possibile utilizzare un altro programma come il seguente: codeproject.com/KB/aspnet/ImportExportCSV.aspx
Bernhard Hofmann

Non è necessario, ma volevo sapere con certezza se sqlcmd poteva effettivamente farlo prima di immergersi in qualche altra utility di esportazione. Una cosa da menzionare è che deve essere gestibile da script.
Ray

Esiste uno strumento aggiuntivo SSMS 2008 che esegue l'output CSV dalle tabelle che può essere personalizzato da dove e ordina per clausole. store.nmally.com/software/sql-server-management-studio-addons/…

Risposte:


140

Puoi eseguire qualcosa del genere:

sqlcmd -S MyServer -d myDB -E -Q "select col1, col2, col3 from SomeTable" 
       -o "MyData.csv" -h-1 -s"," -w 700
  • -h-1 rimuove le intestazioni dei nomi delle colonne dal risultato
  • -s"," imposta il separatore di colonne su,
  • -w 700 imposta la larghezza della riga su 700 caratteri (questo dovrà essere largo quanto la riga più lunga o passerà alla riga successiva)

22
L'avvertenza nel farlo in questo modo è che i tuoi dati potrebbero non contenere virgole.
Sarel Botha,

1
@SarelBotha, puoi aggirare quel problema '""' + col1 + '""' AS col1, racchiuso tra virgolette doppie (raddoppiate) o semplicemente chiamare una procedura memorizzata.
MisterIsaak,

2
@JIsaak Quindi assicurati che i tuoi dati non abbiano virgolette doppie o assicurati di sostituire le virgolette doppie con due virgolette doppie.
Sarel Botha,

1
Qualcuno potrebbe chiarire cosa si deve fare per consentire le virgole all'interno dei dati? Dobbiamo circondare ogni colonna con '""' + ___ + '""'?
Ahmed,

2
Questa risposta è ora obsoleta. Gli script di PowerShell sono più flessibili e possono essere eseguiti in SQL Server come Job Agent.
Clinton Ward,

75

Con PowerShell puoi risolvere il problema ordinatamente eseguendo il piping di Invoke-Sqlcmd in 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 include il modulo SqlServer , che contiene il Invoke-Sqlcmdcmdlet, che avrai anche se hai appena installato SSMS 2016. Prima di ciò, SQL Server 2012 includeva il vecchio modulo SQLPS , che avrebbe cambiato la directory corrente a SQLSERVER:\quando il modulo era usato per la prima volta (tra gli altri bug) quindi, per questo, dovrai cambiare la #Requiresriga sopra in:

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

Per adattare l'esempio per SQL Server 2008 e 2008 R2, rimuovere completamente la #Requiresriga e utilizzare l' utilità sqlps.exe anziché l'host PowerShell standard.

Invoke-Sqlcmd è l'equivalente di PowerShell di sqlcmd.exe. Invece del testo, genera oggetti System.Data.DataRow .

Il -Queryparametro funziona come il -Qparametro di sqlcmd.exe. Passa una query SQL che descriva i dati che desideri esportare.

Il -Databaseparametro funziona come il -dparametro di sqlcmd.exe. Passare il nome del database che contiene i dati da esportare.

Il -Serverparametro funziona come il -Sparametro di sqlcmd.exe. Passare il nome del server che contiene i dati da esportare.

Export-CSV è un cmdlet di PowerShell che serializza oggetti generici su CSV. Viene fornito con PowerShell.

Il -NoTypeInformationparametro elimina l'output aggiuntivo che non fa parte del formato CSV. Per impostazione predefinita, il cmdlet scrive un'intestazione con informazioni sul tipo. Ti consente di conoscere il tipo di oggetto quando lo deserializzi successivamente con Import-Csv, ma confonde gli strumenti che prevedono CSV standard.

Il -Pathparametro funziona come il -oparametro di sqlcmd.exe. Un percorso completo per questo valore è più sicuro se si è bloccati utilizzando il vecchio modulo SQLPS .

Il -Encodingparametro funziona come i parametri -fo -udi sqlcmd.exe. Per impostazione predefinita, Export-Csv genera solo caratteri ASCII e sostituisce tutti gli altri con punti interrogativi. Usa invece UTF8 per preservare tutti i personaggi e rimanere compatibile con la maggior parte degli altri strumenti.

Il vantaggio principale di questa soluzione rispetto a sqlcmd.exe o bcp.exe è che non è necessario hackerare il comando per generare CSV validi. Il cmdlet Export-Csv gestisce tutto per te.

Lo svantaggio principale è che Invoke-Sqlcmdlegge l'intero set di risultati prima di passarlo lungo la pipeline. Assicurarsi di disporre di memoria sufficiente per l'intero set di risultati che si desidera esportare.

Potrebbe non funzionare senza problemi per miliardi di righe. Se questo è un problema, puoi provare gli altri strumenti o creare la tua versione efficiente Invoke-Sqlcmddell'utilizzo della classe System.Data.SqlClient.SqlDataReader .


5
Altre risposte fanno schifo onestamente, questo è l'unico modo per farlo correttamente. Vorrei che fosse più ovvio però.
Shagglez,

1
Su SQL 2008 R2, ho dovuto eseguire lo strumento "sqlps.exe" per utilizzare Invoke-Sqlcmd. Apparentemente ho bisogno di SQL 2012 per usare Import-Module? Funziona sempre all'interno di "sqlps.exe" - vedi questa discussione per i dettagli .
Mister_Tom,

1
@Mister_Tom buon punto. Il modulo SQLPS è stato introdotto con SQL 2012. La risposta ora spiega come adattare l'esempio per le versioni precedenti.
Iain Samuel McLean Elder,

1
@JasonMatney PowerShell è la nuova interfaccia amministrativa per i sistemi Windows, ma molti consigli su SQL Server sono stati pubblicati prima che diventassero standard. Diffondere la parola! :-)
Iain Samuel McLean Elder il

1
Questa risposta fornisce informazioni utili e un modo ALTERNATIVO potente e flessibile per gestire il problema, tuttavia non riesce a rispondere completamente alla domanda originale come è stato specificamente chiesto. Sono anche un fan di PowerShell, ma non lasciamo che l'evangelizzazione si trasformi in isteria. SQLCMD non scomparirà presto.
barbecue

69
sqlcmd -S myServer -d myDB -E -o "MyData.txt" ^
    -Q "select bar from foo" ^
    -W -w 999 -s","

L'ultima riga contiene opzioni specifiche per CSV.

  • -W   rimuovere gli spazi finali da ogni singolo campo
  • -s","   imposta il separatore di colonna sulla virgola (,)
  • -w 999   imposta la larghezza della riga su 999 caratteri

la risposta di scottm è molto vicina a ciò che uso, ma trovo -Wche sia un'aggiunta davvero piacevole: non ho bisogno di tagliare gli spazi bianchi quando consumo il CSV altrove.

Vedi anche il riferimento sqlcmd MSDN . Fa /?vergognare l'output dell'opzione.


19
@sims "set nocount on" all'inizio della query / inputfile
d -_- b

7
Come posso rimuovere la sottolineatura delle intestazioni?
ntombela,

@gugulethun: è possibile eseguire un'unione nella query per posizionare il nome della colonna sulla prima riga.
Nordes,

2
Funziona come un incantesimo, ma se la tua colonna contiene il separatore ottengo un file CSV corrotto ...
Peter

Aggiunto anche questo commento alla risposta accettata ma ... puoi aggirare quel problema '""' + col1 + '""' AS col1, racchiuso tra virgolette doppie (raddoppiate) o semplicemente chiamare una procedura memorizzata.
MisterIsaak,

59

Non è bcpstato pensato per questo?

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

Esegui questo dalla tua riga di comando per verificare la sintassi.

bcp /?

Per esempio:

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]

Si noti che bcpnon è possibile generare intestazioni di colonna.

Vedi: pagina dei documenti dell'utilità bcp .

Esempio dalla pagina sopra:

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

1
ServerName = YourcomputerName \ SQLServerName, solo allora esegue altrimenti l'errore
Hammad Khan,

1
Nel caso in cui si desideri visualizzare la documentazione completa per bcp.exe: technet.microsoft.com/en-us/library/ms162802.aspx
Robert Bernstein,

5
Cosa fai se devi esportare anche i nomi delle colonne come intestazione? Esiste una soluzione generica semplice che utilizza bcp?
Iain Samuel McLean Elder,

@johndacosta grazie mille. Come stamperesti anche le intestazioni di colonna? Non vedo un passaggio facile da nessuna parte. Grazie!
Rachael,

1
bcpnon include l'intestazione (nomi di colonna), ma è ~ 10 volte più veloce di sqlcmd(dalla mia esperienza). Per dati davvero grandi, è possibile utilizzare bcpper ottenere i dati e utilizzare sqlcmd(selezionare top 0 * da ...) per ottenere l'intestazione, quindi combinarli.
YJZ,

12

Una nota per chiunque cerchi di farlo ma ha anche le intestazioni di colonna, questa è la soluzione che ho usato un file batch:

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

Questo genera i risultati iniziali (compresi i separatori ----, ---- tra le intestazioni e i dati) in un file temporaneo, quindi rimuove quella linea filtrandola attraverso findstr. Nota che non è perfetto dal momento che sta filtrando: -,-non funzionerà se c'è solo una colonna nell'output e filtrerà anche le righe legittime che contengono quella stringa.


1
Utilizzare invece il seguente filtro: findstr / r / v ^ \ - [, \ -] * $> output.csv Per qualche motivo simle ^ [, \ -] * $ corrisponde a tutte le righe.
Vladimir Korolev,

3
Inserisci sempre una regex con ^ tra virgolette doppie, o otterrai strani risultati, perché ^ è un carattere di escape per cmd.exe. Entrambi i regex sopra non funzionano correttamente, per quanto posso dire, ma questo fa: findstr / r / v "^ - [-,] * -. $" (Il. Prima di $ sembra essere necessario quando si prova con eco, ma potrebbe non essere per l'output sqlcmd)
JimG

C'è anche un problema con le date che hanno 2 spazi tra data e ora anziché una. Quando si tenta di aprire CSV in Excel, viene visualizzato come 00: 00.0. Un modo semplice per risolvere questo sarebbe quello di cercare e sostituire tutti "" con "" sul posto utilizzando SED. Il comando da aggiungere allo script sarebbe: SED -i "s / / / g" output.csv. Maggiori informazioni su SED gnuwin32.sourceforge.net/packages/sed.htm
PollusB

questa è la risposta corretta, funziona perfettamente con l'aggiunta di @ JimG. Ho usato il punto e virgola per il separatore e un file sql per l'input, il file conteneva la parte oncount e ansi_warning off e l'opzione -W per la rimozione dello spazio
robotik,

1

Questa risposta si basa sulla soluzione di @ iain-elder, che funziona bene ad eccezione del caso del database di grandi dimensioni (come sottolineato nella sua soluzione). L'intero tavolo deve adattarsi alla memoria del tuo sistema, e per me questa non era un'opzione. Ho il sospetto che la soluzione migliore userebbe System.Data.SqlClient.SqlDataReader e un serializzatore CSV personalizzato ( vedi qui per un esempio ) o un'altra lingua con un driver MS SQL e serializzazione CSV. Nello spirito della domanda originale che probabilmente stava cercando una soluzione senza dipendenze, il codice PowerShell di seguito ha funzionato per me. È molto lento e inefficiente soprattutto nell'istanza dell'array $ data e nella chiamata Export-Csv in modalità append per ogni $ chunk_size righe.

$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

Opzione alternativa con BCP:

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

1

Di solito sqlcmdviene fornito con l' bcputilità (come parte di mssql-tools) che esporta in CSV per impostazione predefinita.

Uso:

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

Per esempio:

bcp.exe MyTable out data.csv

Per scaricare tutte le tabelle nei corrispondenti file CSV, ecco lo 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

0

Una risposta sopra quasi risolto per me, ma non crea correttamente un CSV analizzato.

Ecco la mia versione:

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

Qualcuno che afferma che sqlcmdè obsoleto a favore di alcune alternative di PowerShell sta dimenticando che sqlcmdnon è solo per Windows. Sono su Linux (e quando su Windows evito comunque PS).

Detto questo, trovo bcppiù facile.


0

Poiché i seguenti 2 motivi, dovresti eseguire la mia soluzione in CMD:

  1. Potrebbero essere presenti virgolette doppie nella query
  2. Nome utente e password di accesso sono talvolta necessari per eseguire una query su un'istanza remota di 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

-2

Puoi farlo in un modo hacker. Attento usando l' sqlcmdhack. Se i dati hanno virgolette doppie o virgole si verificheranno problemi.

È possibile utilizzare uno script semplice per farlo correttamente:

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' 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!")

Fonte: scrittura dell'output SQL su CSV con VBScript .


1
Una spiegazione per il downvote sarebbe buona. La mia risposta è corretta: non puoi farlo con sqlcmd. Ho anche offerto un modo alternativo per svolgere il compito.
Sarel Botha,

4
Il downvote è perché chiaramente PUOI fare ciò che OP richiede con sqlcmd.
Brian Driscoll,

2
@BrianDriscoll, Non ha detto che non si può farcela sqlcmd, stavamo solo affermando il fatto che sqlcmdnon sta sfuggendo correttamente alla virgola, essendo a malapena utilizzabile per qualsiasi output CSV serio .
Sebastian,

L'ho detto, ma mi sono stancato dei voti negativi, quindi ho modificato la mia risposta. Renderò la mia modifica più veritiera.
Sarel Botha,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.