Como exportar dados como formato CSV do SQL Server usando sqlcmd?

136

Eu posso facilmente despejar dados em um arquivo de texto, como:

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

No entanto, observei os arquivos de ajuda, SQLCMDmas não vi uma opção específica para CSV.

Existe uma maneira de despejar dados de uma tabela em um arquivo de texto CSV usando SQLCMD?

Raio
fonte
Deve ser isso via sqlcmd, ou você poderia usar outro programa, como o seguinte: codeproject.com/KB/aspnet/ImportExportCSV.aspx
Bernhard Hofmann
Não precisa ser, mas eu queria saber com certeza se o sqlcmd poderia ou não fazer isso antes de mergulhar em outro utilitário de exportação. Uma coisa a mencionar é que ele precisa ser scriptável.
Ray
Existe uma ferramenta de suplemento do SSMS 2008 que gera saída CSV de suas tabelas que podem ser personalizadas por cláusulas where e ordenadas por store.nmally.com/software/sql-server-management-studio-addons/…

Respostas:

140

Você pode executar algo parecido com isto:

sqlcmd -S MyServer -d myDB -E -Q "select col1, col2, col3 from SomeTable" 
       -o "MyData.csv" -h-1 -s"," -w 700
  • -h-1 remove os cabeçalhos do nome da coluna do resultado
  • -s"," define o separador de coluna como,
  • -w 700 define a largura da linha para 700 caracteres (isso precisará ser tão largo quanto a linha mais longa ou será agrupado na próxima linha)
escocês
fonte
22
A ressalva em fazê-lo dessa maneira é que seus dados podem não conter vírgulas.
22812 Sarel Botha
1
@ SararBotha, você pode contornar esse problema com '""' + col1 + '""' AS col1aspas duplas (dobradas) ou simplesmente chamar um procedimento armazenado.
MisterIsaak
2
@JIsaak Certifique-se de que seus dados não tenham aspas duplas ou substitua-as por duas aspas.
Sarel Botha 12/12
1
Alguém poderia esclarecer o que deve ser feito para permitir vírgulas dentro dos dados? Temos que cercar todas as colunas com '""' + ___ + '"" "'?
Ahmed
2
Esta resposta está desatualizada. Os scripts do PowerShell são mais flexíveis e podem ser executados no SQL Server como um Job Agent.
Clinton Ward
75

Com o PowerShell, você pode resolver o problema perfeitamente, canalizando Invoke-Sqlcmd para Export-Csv.

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

O SQL Server 2016 inclui o módulo SqlServer , que contém o Invoke-Sqlcmdcmdlet, que você terá mesmo se instalar o SSMS 2016. Antes disso, o SQL Server 2012 incluía o antigo módulo SQLPS , que alteraria o diretório atual para SQLSERVER:\quando o módulo fosse usado pela primeira vez (entre outros bugs), portanto, para isso, você precisará alterar a #Requireslinha acima para:

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 o exemplo ao SQL Server 2008 e 2008 R2, remova #Requirescompletamente a linha e use o utilitário sqlps.exe em vez do host padrão do PowerShell.

Invoke-Sqlcmd é o equivalente do PowerShell ao sqlcmd.exe. Em vez de texto, ele gera objetos System.Data.DataRow .

O -Queryparâmetro funciona como o -Qparâmetro sqlcmd.exe. Passe uma consulta SQL que descreva os dados que você deseja exportar.

O -Databaseparâmetro funciona como o -dparâmetro sqlcmd.exe. Passe o nome do banco de dados que contém os dados a serem exportados.

O -Serverparâmetro funciona como o -Sparâmetro sqlcmd.exe. Passe o nome do servidor que contém os dados a serem exportados.

Export-CSV é um cmdlet do PowerShell que serializa objetos genéricos em CSV. É fornecido com o PowerShell.

O -NoTypeInformationparâmetro suprime a saída extra que não faz parte do formato CSV. Por padrão, o cmdlet grava um cabeçalho com informações de tipo. Ele permite que você saiba o tipo do objeto ao desserializá-lo posteriormente Import-Csv, mas confunde ferramentas que esperam um CSV padrão.

O -Pathparâmetro funciona como o -oparâmetro sqlcmd.exe. Um caminho completo para esse valor é mais seguro se você estiver preso usando o antigo módulo SQLPS .

O -Encodingparâmetro funciona como os parâmetros -fou -udo sqlcmd.exe. Por padrão, Export-Csv gera apenas caracteres ASCII e substitui todos os outros por pontos de interrogação. Use UTF8 para preservar todos os caracteres e permanecer compatível com a maioria das outras ferramentas.

A principal vantagem dessa solução sobre o sqlcmd.exe ou o bcp.exe é que você não precisa hackear o comando para gerar um CSV válido. O cmdlet Export-Csv lida com tudo isso para você.

A principal desvantagem é que Invoke-Sqlcmdlê todo o conjunto de resultados antes de transmiti-lo ao longo do pipeline. Verifique se você possui memória suficiente para todo o conjunto de resultados que deseja exportar.

Pode não funcionar sem problemas por bilhões de linhas. Se isso for um problema, você pode tentar as outras ferramentas ou implementar sua própria versão eficiente do Invoke-Sqlcmduso da classe System.Data.SqlClient.SqlDataReader .

Iain Samuel McLean Elder
fonte
5
Outras respostas são péssimas, essa é a única maneira de fazê-lo corretamente. Eu gostaria que fosse mais óbvio.
Shagglez
1
No SQL 2008 R2, tive que executar a ferramenta "sqlps.exe" para usar o Invoke-Sqlcmd. Aparentemente, preciso do SQL 2012 para usar o Import-Module? De qualquer forma, ele funciona no "sqlps.exe" - consulte este tópico para obter detalhes .
Mister_Tom
1
@Mister_Tom bom ponto. O módulo SQLPS foi introduzido no SQL 2012. A resposta agora explica como adaptar o exemplo para edições mais antigas.
Iain Samuel McLean Elder
1
@JasonMatney PowerShell é a nova interface administrativa para sistemas Windows, mas muitos conselhos do SQL Server foram publicados antes de se tornarem padrão. Espalhe a palavra! :-)
Iain Samuel McLean Elder
1
Esta resposta fornece informações úteis e uma maneira ALTERNATIVA poderosa e flexível de lidar com o problema, mas falha completamente em responder à pergunta original, como foi especificamente solicitado. Também sou fã de powerhell, mas não vamos deixar o evangelismo se transformar em histeria. O SQLCMD não será desativado tão cedo.
churrasco
69
sqlcmd -S myServer -d myDB -E -o "MyData.txt" ^
    -Q "select bar from foo" ^
    -W -w 999 -s","

A última linha contém opções específicas para CSV.

  • -W   remover espaços à direita de cada campo individual
  • -s","   define o separador de coluna como vírgula (,)
  • -w 999   define a largura da linha para 999 caracteres

A resposta da scottm está muito próxima do que eu uso, mas acho -Wque é uma adição muito boa: não preciso aparar espaços em branco quando consumir o CSV em outro lugar.

Consulte também a referência sqlcmd do MSDN . Isso /?envergonha a saída da opção.

ESV
fonte
19
@sims "set nocount on" no início da query / inputfile
d -_- b
7
Como remover o sublinhado dos cabeçalhos?
Ntombela 26/05
@gugulethun: Você pode fazer uma união na sua consulta para colocar o nome da coluna na primeira linha.
Nordes
2
Isso funciona como um encanto, mas se a coluna contiver o separador, recebo um arquivo csv corrompido ... #
Peter
Também foi adicionado este comentário à resposta aceita, mas ... você pode contornar esse problema com '""' + col1 + '""' AS col1aspas duplas (dobradas) ou apenas chamar um procedimento armazenado.
MisterIsaak
59

Isso não bcpfoi feito para isso?

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

Execute isso na sua linha de comando para verificar a sintaxe.

bcp /?

Por exemplo:

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]

Observe que bcpnão é possível gerar cabeçalhos de coluna.

Consulte: página de documentos do utilitário bcp .

Exemplo da página acima:

bcp.exe MyTable out "D:\data.csv" -T -c -C 65001 -t , ...
johndacostaa
fonte
1
ServerName = YourComputerName \ SQLServerName, só então ele executa outra forma erro
Hammad Khan
1
No caso de você quiser ver a documentação completa para bcp.exe: technet.microsoft.com/en-us/library/ms162802.aspx
Robert Bernstein
5
O que você faz se precisar exportar os nomes das colunas como cabeçalho também? Existe uma solução genérica direta usando o bcp?
Iain Samuel McLean Elder
@johndacosta muito obrigado. Como você imprimiria os cabeçalhos das colunas também? Não estou vendo uma mudança fácil para isso em nenhum lugar. Obrigado!
224 Rachael
1
bcpnão inclui o cabeçalho (nomes das colunas), mas é 10 vezes mais rápido do que sqlcmd(pela minha experiência). Para dados realmente grandes, você pode usar bcppara obtê- los e usar sqlcmd(selecione 0 * superior de ...) para obter o cabeçalho e combiná-los.
YJZ 4/18
12

Uma observação para quem quer fazer isso, mas também possui os cabeçalhos das colunas, esta é a solução que usei um arquivo em 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

Isso gera os resultados iniciais (incluindo os separadores ----, ---- entre os cabeçalhos e os dados) em um arquivo temporário e remove a linha, filtrando-a através de findstr. Observe que não é perfeito, pois está filtrando -,-- não funcionará se houver apenas uma coluna na saída e também filtrará as linhas legítimas que contêm essa string.

Rudismo
fonte
1
Use o seguinte filtro: findstr / r / v ^ \ - [, \ -] * $> output.csv Por alguma razão, simle ^ [, \ -] * $ corresponde a todas as linhas.
Vladimir Korolev
3
Sempre coloque um regex com ^ dentro de aspas duplas ou obterá resultados estranhos, porque ^ é um caractere de escape para o cmd.exe. Ambas as expressões regulares acima não funcionam corretamente, até onde eu sei, mas isso funciona: findstr / r / v "^ - [-,] * -. $" (O. Antes do $ parece ser necessário ao testar com echo, mas talvez não para de saída sqlcmd)
JimG
Também há um problema com datas com 2 espaços entre data e hora em vez de um. Quando você tenta abrir o CSV no Excel, ele aparece como 00: 00.0. Uma maneira fácil de resolver isso seria procurar e substituir todos "" por "" no local usando o SED. O comando a ser adicionado ao seu script seria: SED -i "s / / / g" output.csv. Mais sobre o SED gnuwin32.sourceforge.net/packages/sed.htm #
PollusB
esta é a resposta correta, funciona perfeitamente com a adição do @ JimG. Eu usei ponto e vírgula como separador, e um arquivo sql para a entrada, o arquivo continha o noncount on e off ansi_warning lado, ea chave -W para a remoção do espaço
robotik
1

Esta resposta baseia-se na solução do @ iain-elder, que funciona bem, exceto no caso de banco de dados grande (como apontado em sua solução). A tabela inteira precisa caber na memória do seu sistema, e para mim isso não era uma opção. Eu suspeito que a melhor solução usaria o System.Data.SqlClient.SqlDataReader e um serializador CSV personalizado ( veja aqui um exemplo ) ou outro idioma com um driver MS SQL e serialização CSV. No espírito da pergunta original, que provavelmente estava procurando uma solução sem dependência, o código do PowerShell abaixo funcionou para mim. É muito lento e ineficiente, especialmente em instanciar o array de dados $ e chamar Export-Csv no modo de acréscimo para cada linha de tamanho $ 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
fonte
1

Opção alternativa com BCP:

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

Geralmente sqlcmdvem com o bcputilitário (como parte de mssql-tools) que exporta para CSV por padrão.

Uso:

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

Por exemplo:

bcp.exe MyTable out data.csv

Para despejar todas as tabelas nos arquivos CSV correspondentes, aqui está o 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
fonte
0

Uma resposta acima quase o resolveu para mim, mas não cria corretamente um CSV analisado.

Aqui está a minha versão:

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

Alguém dizendo que sqlcmdestá desatualizado em favor de alguma alternativa do PowerShell está esquecendo que sqlcmdnão é apenas para o Windows. Estou no Linux (e quando no Windows evito PS de qualquer maneira).

Dito tudo isso, acho bcpmais fácil.

Hack-R
fonte
0

Desde os 2 motivos a seguir, você deve executar minha solução no CMD:

  1. Pode haver aspas duplas na consulta
  2. Às vezes, é necessário o nome de usuário e senha de login para consultar uma instância remota do 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
fonte
-2

Você pode fazer isso de uma maneira hackeada. Cuidado usando o sqlcmdhack. Se os dados tiverem aspas ou vírgulas, você terá problemas.

Você pode usar um script simples para fazer isso corretamente:

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

Fonte: Gravando saída SQL em CSV com VBScript .

Sarel Botha
fonte
1
Uma explicação para o voto negativo seria legal. Minha resposta está correta: você não pode fazer isso com o sqlcmd. Também ofereci uma maneira alternativa de realizar a tarefa.
Sarel Botha
4
O voto negativo é porque você claramente PODE fazer o que o OP está pedindo com o sqlcmd.
Brian Driscoll
2
@BrianDriscoll, ele não disse que não se pode fazer isso sqlcmd, estávamos apenas declarando o fato de que sqlcmdnão está escapando adequadamente da vírgula, dificilmente sendo útil para qualquer saída séria de CSV.
Sebastian
Eu disse isso, mas me cansei dos votos negativos, então editei minha resposta. Tornarei minha edição mais verdadeira.
Sarel Botha