Bagaimana cara mengekspor data sebagai format CSV dari SQL Server menggunakan sqlcmd?

136

Saya dapat dengan mudah membuang data ke file teks seperti:

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

Namun, saya telah melihat file bantuan untuk SQLCMDtetapi belum melihat opsi khusus untuk CSV.

Apakah ada cara untuk membuang data dari tabel ke file teks CSV menggunakan SQLCMD?

sinar
sumber
Harus melalui sqlcmd, atau Anda dapat menggunakan program lain seperti berikut: codeproject.com/KB/aspnet/ImportExportCSV.aspx
Bernhard Hofmann
Tidak harus tetapi saya ingin tahu pasti apakah sqlcmd benar-benar bisa melakukan ini sebelum menyelam ke beberapa utilitas ekspor lainnya. Satu hal yang perlu disebutkan adalah bahwa skrip harus dibuat.
Ray
Ada alat tambahan SSMS 2008 yang melakukan keluaran CSV dari tabel Anda yang dapat dikustomisasi di mana dan dipesan oleh klausa. store.nmally.com/software/sql-server-management-studio-addons/…

Jawaban:

140

Anda dapat menjalankan sesuatu seperti ini:

sqlcmd -S MyServer -d myDB -E -Q "select col1, col2, col3 from SomeTable" 
       -o "MyData.csv" -h-1 -s"," -w 700
  • -h-1 menghapus tajuk nama kolom dari hasilnya
  • -s"," set pemisah kolom menjadi,
  • -w 700 set lebar baris menjadi 700 karakter (ini harus selebar baris terpanjang atau akan membungkus ke baris berikutnya)
scottm
sumber
22
Peringatan dengan melakukannya dengan cara ini adalah bahwa data Anda mungkin tidak mengandung koma.
Sarel Botha
1
@SarelBotha, Anda dapat mengatasi masalah itu dengan '""' + col1 + '""' AS col1, membungkus (dua kali lipat) tanda kutip ganda atau hanya memanggil prosedur tersimpan.
MisterIsaak
2
@JIsaak Kemudian pastikan data Anda tidak memiliki tanda kutip ganda atau pastikan untuk mengganti tanda kutip ganda Anda dengan dua tanda kutip ganda.
Sarel Botha
1
Bisakah seseorang mengklarifikasi apa yang harus dilakukan untuk memungkinkan koma di dalam data? Apakah kita harus mengelilingi setiap kolom dengan '""' + ___ + '""'?
Ahmed
2
Jawaban ini sekarang sudah usang. Skrip PowerShell lebih fleksibel dan dapat dijalankan di SQL Server sebagai Agen Pekerjaan.
Clinton Ward
75

Dengan PowerShell Anda dapat menyelesaikan masalah dengan rapi dengan mem-pipkan Invoke-Sqlcmd ke 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 termasuk modul SqlServer , yang berisi Invoke-Sqlcmdcmdlet, yang akan Anda miliki bahkan jika Anda baru saja menginstal SSMS 2016. Sebelum itu, SQL Server 2012 menyertakan modul SQLPS lama , yang akan mengubah direktori saat ini SQLSERVER:\ketika modul itu pertama kali digunakan (di antara bug lain) jadi untuk itu, Anda harus mengubah #Requiresbaris di atas menjadi:

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

Untuk mengadaptasi contoh untuk SQL Server 2008 dan 2008 R2, hapus #Requiresbaris seluruhnya dan gunakan utilitas sqlps.exe alih-alih host PowerShell standar.

Invoke-Sqlcmd adalah PowerShell yang setara dengan sqlcmd.exe. Alih-alih teks itu output objek System.Data.DataRata .

The -Queryparameter bekerja seperti -Qparameter sqlcmd.exe. Berikan SQL query yang menjelaskan data yang ingin Anda ekspor.

The -Databaseparameter bekerja seperti -dparameter sqlcmd.exe. Berikan nama database yang berisi data yang akan diekspor.

The -Serverparameter bekerja seperti -Sparameter sqlcmd.exe. Berikan nama server yang berisi data yang akan diekspor.

Ekspor-CSV adalah cmdlet PowerShell yang membuat serialisasi objek umum ke CSV. Ini dikirimkan dengan PowerShell.

The -NoTypeInformationparameter menekan keluaran tambahan yang bukan merupakan bagian dari format CSV. Secara default cmdlet menulis header dengan informasi jenis. Ini memungkinkan Anda mengetahui jenis objek ketika Anda deserialize nanti dengan Import-Csv, tetapi membingungkan alat yang mengharapkan CSV standar.

The -Pathparameter bekerja seperti -oparameter sqlcmd.exe. Jalur lengkap untuk nilai ini paling aman jika Anda terjebak menggunakan modul SQLPS lama .

The -Encodingparameter bekerja seperti -fatau -uparameter sqlcmd.exe. Secara standar Export-Csv hanya menghasilkan karakter ASCII dan mengganti yang lainnya dengan tanda tanya. Gunakan UTF8 sebagai gantinya untuk mempertahankan semua karakter dan tetap kompatibel dengan sebagian besar alat lainnya.

Keuntungan utama dari solusi ini daripada sqlcmd.exe atau bcp.exe adalah Anda tidak perlu meretas perintah untuk mengeluarkan CSV yang valid. Cmdlet Export-Csv menangani semuanya untuk Anda.

Kerugian utama adalah Invoke-Sqlcmdmembaca seluruh hasil yang ditetapkan sebelum melewati sepanjang pipa. Pastikan Anda memiliki cukup memori untuk seluruh rangkaian hasil yang ingin Anda ekspor.

Ini mungkin tidak berfungsi dengan baik untuk miliaran baris. Jika itu masalah, Anda bisa mencoba alat lain, atau memutar versi efisien Anda sendiri Invoke-Sqlcmdmenggunakan kelas System.Data.SqlClient.SqlDataReader .

Iain Samuel McLean Elder
sumber
5
Jawaban lain jujur ​​mengisap, ini adalah satu-satunya cara untuk melakukannya dengan benar. Saya berharap itu lebih jelas.
Shagglez
1
Pada SQL 2008 R2, saya harus menjalankan alat "sqlps.exe" untuk menggunakan Invoke-Sqlcmd. Rupanya saya perlu SQL 2012 untuk menggunakan Modul Impor? Anway berfungsi dalam "sqlps.exe" - lihat utas ini untuk detailnya .
Mister_Tom
1
@Mister_Tom poin bagus. Modul SQLPS diperkenalkan dengan SQL 2012. Jawabannya sekarang menjelaskan bagaimana mengadaptasi contoh untuk edisi yang lebih lama.
Iain Samuel McLean Penatua
1
@JasonMatney PowerShell adalah antarmuka administratif baru untuk sistem Windows, tetapi banyak saran SQL Server diterbitkan sebelum menjadi standar. Sebarkan berita! :-)
Penatua Iain Samuel McLean
1
Jawaban ini memberikan informasi yang bermanfaat dan cara ALTERNATIF yang kuat dan fleksibel untuk menangani masalah ini, namun tidak sepenuhnya menjawab pertanyaan awal seperti yang ditanyakan secara khusus. Saya juga penggemar fanatik, tapi jangan biarkan penginjilan berubah menjadi histeria. SQLCMD tidak akan pergi dalam waktu dekat.
Barbekyu
69
sqlcmd -S myServer -d myDB -E -o "MyData.txt" ^
    -Q "select bar from foo" ^
    -W -w 999 -s","

Baris terakhir berisi opsi spesifik CSV.

  • -W   menghapus spasi tambahan dari setiap bidang individu
  • -s","   set pemisah kolom ke koma (,)
  • -w 999   set lebar baris menjadi 999 karakter

Jawaban scottm sangat dekat dengan apa yang saya gunakan, tetapi saya menemukan itu -Wmenjadi tambahan yang sangat bagus: Saya tidak perlu memotong spasi ketika saya mengkonsumsi CSV di tempat lain.

Juga lihat referensi MSDN sqlcmd . Itu membuat /?output opsi menjadi malu.

ESV
sumber
19
@sims "set nocount on" di awal kueri / inputfile
d -_- b
7
Bagaimana saya bisa menghapus garis bawah pada Header?
ntombela
@gugulethun: Anda bisa melakukan penyatuan dalam kueri Anda untuk meletakkan nama kolom pada baris pertama.
Nordes
2
Ini berfungsi seperti pesona, tetapi jika kolom Anda berisi pemisah saya mendapatkan file csv yang rusak ...
Peter
Menambahkan komentar ini ke jawaban yang diterima juga, tetapi ... Anda dapat mengatasi masalah itu dengan '""' + col1 + '""' AS col1, membungkus (menggandakan) tanda kutip ganda atau hanya memanggil prosedur tersimpan.
MisterIsaak
59

Apakah ini tidak bcpdimaksudkan untuk?

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

Jalankan ini dari baris perintah Anda untuk memeriksa sintaks.

bcp /?

Sebagai contoh:

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]

Harap perhatikan bahwa bcptidak dapat menampilkan tajuk kolom.

Lihat: halaman bcp Utility docs.

Contoh dari halaman di atas:

bcp.exe MyTable out "D:\data.csv" -T -c -C 65001 -t , ...
johndacostaa
sumber
1
ServerName = YourcomputerName \ SQLServerName, hanya setelah itu mengeksekusi kesalahan
Hammad Khan
1
Dalam kasus Anda ingin melihat dokumentasi lengkap untuk bcp.exe: technet.microsoft.com/en-us/library/ms162802.aspx
Robert Bernstein
5
Apa yang Anda lakukan jika Anda perlu mengekspor nama kolom sebagai tajuk juga? Apakah ada solusi generik langsung menggunakan bcp?
Iain Samuel McLean Penatua
@ johndacosta terima kasih banyak. Bagaimana Anda mencetak tajuk kolom juga? Saya tidak melihat peralihan yang mudah ke mana pun. Terima kasih!
Rachael
1
bcptidak termasuk header (nama kolom), tetapi ~ 10X lebih cepat dari sqlcmd(dari pengalaman saya). Untuk data yang sangat besar, Anda bisa menggunakan bcpuntuk mendapatkan data, dan menggunakan sqlcmd(pilih top 0 * dari ...) untuk mendapatkan tajuk, lalu gabungkan.
YJZ
12

Catatan untuk siapa pun yang ingin melakukan ini tetapi juga memiliki tajuk kolom, ini adalah solusi yang saya gunakan 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

Ini menampilkan hasil awal (termasuk pemisah ----, ---- antara header dan data) ke dalam file temp, kemudian menghapus baris itu dengan memfilternya melalui findstr. Perhatikan bahwa itu tidak sempurna karena memfilter -,-— itu tidak akan berfungsi jika hanya ada satu kolom di output, dan itu juga akan menyaring garis yang sah yang berisi string itu.

Rudism
sumber
1
Gunakan filter berikut sebagai gantinya: findstr / r / v ^ \ - [, \ -] * $> output.csv Untuk beberapa alasan simle ^ [, \ -] * $ cocok dengan semua baris.
Vladimir Korolev
3
Selalu letakkan regex dengan ^ di dalam tanda kutip ganda, atau Anda mendapatkan hasil yang aneh, karena ^ adalah karakter pelarian untuk cmd.exe. Kedua regex di atas tidak berfungsi dengan baik, sejauh yang saya tahu, tetapi ini tidak: findstr / r / v "^ - [-,] * -. $" (Sebelum $ tampaknya diperlukan saat pengujian dengan gema, tetapi mungkin tidak untuk output sqlcmd)
JimG
Ada juga masalah dengan tanggal memiliki 2 spasi antara tanggal dan waktu, bukan satu. Ketika Anda mencoba untuk membuka CSV di Excel, itu akan ditampilkan sebagai 00: 00.0. Cara mudah untuk menyelesaikan ini adalah dengan mencari dan mengganti semua "" dengan "" di tempat menggunakan SED. Perintah untuk menambahkan skrip Anda adalah: SED -i "s / / / g" output.csv. Lebih lanjut tentang SED gnuwin32.sourceforge.net/packages/sed.htm
PollusB
ini jawaban yang benar, bekerja sempurna dengan penambahan @ JimG. Saya menggunakan titik koma untuk separator, dan file sql untuk input, file berisi bagian noncount dan ansi_warning off, dan -W beralih untuk penghapusan ruang
robotik
1

Jawaban ini dibangun berdasarkan solusi dari @ iain-elder, yang berfungsi dengan baik kecuali untuk kasus basis data yang besar (seperti yang ditunjukkan dalam solusinya). Seluruh tabel harus sesuai dengan memori sistem Anda, dan bagi saya ini bukan pilihan. Saya menduga solusi terbaik akan menggunakan System.Data.SqlClient.SqlDataReader dan serializer CSV kustom ( lihat di sini untuk contoh ) atau bahasa lain dengan driver MS SQL dan serialisasi CSV. Dalam semangat pertanyaan asli yang mungkin mencari solusi tanpa ketergantungan, kode PowerShell di bawah ini berfungsi untuk saya. Ini sangat lambat dan tidak efisien terutama dalam instantiating $ data array dan memanggil Export-Csv dalam mode append untuk setiap baris $ 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
sumber
1

Opsi alternatif dengan BCP:

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

Biasanya sqlcmddilengkapi dengan bcputilitas (sebagai bagian dari mssql-tools) yang mengekspor ke CSV secara default.

Pemakaian:

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

Sebagai contoh:

bcp.exe MyTable out data.csv

Untuk membuang semua tabel ke file CSV yang sesuai, berikut adalah skrip 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
sumber
0

Jawaban di atas hampir menyelesaikannya untuk saya tetapi tidak membuat CSV yang diuraikan dengan benar.

Ini versi saya:

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

Seseorang yang mengatakan bahwa sqlcmditu sudah ketinggalan zaman mendukung beberapa alternatif PowerShell lupa bahwa sqlcmditu tidak hanya untuk Windows. Saya di Linux (dan ketika di Windows saya tetap menghindari PS).

Setelah mengatakan semua itu, saya merasa bcplebih mudah.

Hack-R
sumber
0

Karena mengikuti 2 alasan, Anda harus menjalankan solusi saya di CMD:

  1. Mungkin ada tanda kutip ganda dalam kueri
  2. Nama pengguna dan kata sandi masuk kadang-kadang diperlukan untuk meminta contoh SQL Server jarak jauh

    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
sumber
-2

Anda bisa melakukannya dengan cara hack. Hati-hati menggunakan sqlcmdperetasan. Jika data memiliki tanda kutip ganda atau koma Anda akan mengalami masalah.

Anda dapat menggunakan skrip sederhana untuk melakukannya dengan benar:

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

Sumber: Menulis output SQL ke CSV dengan VBScript .

Sarel Botha
sumber
1
Penjelasan untuk downvote akan menyenangkan. Jawaban saya benar: Anda tidak dapat melakukan ini dengan sqlcmd. Saya juga menawarkan cara alternatif untuk menyelesaikan tugas.
Sarel Botha
4
Downvote adalah karena Anda jelas BISA melakukan apa yang diminta OP dengan sqlcmd.
Brian Driscoll
2
@BrianDriscoll, Dia tidak mengatakan bahwa kita tidak dapat melakukannya dengan sqlcmd, kita hanya menyatakan fakta bahwa sqlcmdtidak lolos dari koma, sehingga hampir tidak dapat digunakan untuk output CSV yang serius .
Sebastian
Saya memang mengatakan itu, tetapi bosan dengan downvotes, jadi saya mengedit jawaban saya. Saya akan menjadikan hasil edit saya lebih jujur.
Sarel Botha