sqlcmdを使用してSQL ServerからCSV形式でデータをエクスポートする方法


136

次のようなテキストファイルにデータを簡単にダンプできます。

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

ただし、ヘルプファイルを確認しましSQLCMDたが、CSV専用のオプションは確認していません。

を使用してテーブルからCSVテキストファイルにデータをダンプする方法はありますSQLCMDか?


これはsqlcmd経由である必要があります。または、次のような別のプログラムを使用できます。codeproject.com
Bernhard Hofmann

必ずしもそうである必要はありませんが、他のエクスポートユーティリティを使用する前に、sqlcmdが実際にこれを実行できるかどうかを確認したかったのです。言及すべきことの1つは、スクリプト可能である必要があることです。
レイ

where句でカスタマイズしたり、order by句でカスタマイズしたりできる、テーブルからのCSV出力を行うSSMS 2008アドインツールがあります。store.nmally.com/software/sql-server-management-studio-addons/...

回答:


140

次のように実行できます。

sqlcmd -S MyServer -d myDB -E -Q "select col1, col2, col3 from SomeTable" 
       -o "MyData.csv" -h-1 -s"," -w 700
  • -h-1 結果から列名ヘッダーを削除します
  • -s"," 列セパレーターをに設定し、
  • -w 700 行幅を700文字に設定します(これは最長の行と同じ幅にする必要があります。そうしないと、次の行に折り返されます)

22
この方法で行う場合の注意点は、データにコンマが含まれていない可能性があることです。
Sarel Botha 2012年

1
@SarelBotha、'""' + col1 + '""' AS col1(二重の)二重引用符で囲むか、ストアドプロシージャを呼び出すだけで、この問題を回避できます。
MisterIsaak

2
@JIsaak次に、データに二重引用符がないことを確認するか、二重引用符を2つの二重引用符で置き換えてください。
Sarel Botha

1
誰かがデータ内にコンマを許可するために何をすべきかを明確にできますか?すべての列を '""' + ___ + '""'で囲む必要がありますか?
アーメド2015

2
この回答は古くなっています。PowerShellスクリプトはより柔軟で、SQL Serverでジョブエージェントとして実行できます。
クリントンウォード

75

PowerShellを使用すると、Invoke-Sqlcmdを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年には含まれたSQLServer含むモジュール、Invoke-Sqlcmdあなたはちょうどその前にSSMS 2016をインストールした場合でも必要がありますコマンドレットを、SQL Server 2012のは、古い含まSQLPSのモジュールにカレントディレクトリを変更します、SQLSERVER:\モジュールだったときに(他のバグの中でも特に)最初に使用されたので、そのためには、#Requires上記の行を次のように変更する必要があります。

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

この例をSQL Server 2008および2008 R2に適合させるには、#Requires行を完全に削除し、標準のPowerShellホストの代わりにsqlps.exeユーティリティを使用します。

Invoke-Sqlcmdは、sqlcmd.exeと同等のPowerShellです。テキストの代わりにSystem.Data.DataRowオブジェクトを出力します。

この-Queryパラメーター-Qは、sqlcmd.exe のパラメーターと同様に機能します。エクスポートするデータを説明するSQLクエリを渡します。

この-Databaseパラメーター-dは、sqlcmd.exe のパラメーターと同様に機能します。エクスポートするデータを含むデータベースの名前を渡します。

この-Serverパラメーター-Sは、sqlcmd.exe のパラメーターと同様に機能します。エクスポートするデータを含むサーバーの名前を渡します。

Export-CSVは、汎用オブジェクトをCSVにシリアル化するPowerShellコマンドレットです。PowerShellに付属しています。

この-NoTypeInformationパラメーターは、CSV形式の一部ではない余分な出力を抑制します。デフォルトでは、コマンドレットはタイプ情報を含むヘッダーを書き込みます。後で逆シリアル化するときにオブジェクトのタイプを知ることができますが、Import-Csv標準のCSVを期待するツールを混乱させます。

この-Pathパラメーター-oは、sqlcmd.exe のパラメーターと同様に機能します。古いSQLPSモジュールを使用している場合は、この値の完全パスが最も安全です。

-Encodingパラメータは以下のように動作します-f-usqlcmd.exeのパラメータ。デフォルトでは、Export-CsvはASCII文字のみを出力し、その他すべてを疑問符に置き換えます。代わりにUTF8を使用して、すべての文字を保持し、他のほとんどのツールとの互換性を維持してください。

sqlcmd.exeまたはbcp.exeに対するこのソリューションの主な利点は、有効なCSVを出力するためにコマンドをハックする必要がないことです。Export-Csvコマンドレットがすべてを処理します。

主な欠点はInvoke-Sqlcmd、パイプラインに沿って渡す前に結果セット全体を読み取ることです。エクスポートする結果セット全体に対して十分なメモリがあることを確認してください。

数十億行ではスムーズに機能しない可能性があります。それが問題である場合は、他のツールを試すか、System.Data.SqlClient.SqlDataReaderクラスをInvoke-Sqlcmd使用し て独自の効率的なバージョンをロールすることができます。


5
他の答えは正直に言えば、これが正しく行う唯一の方法です。それがもっと明白だったらいいのに。
Shagglez、2014

1
SQL 2008 R2では、Invoke-Sqlcmdを使用するために「sqlps.exe」ツールを実行する必要がありました。Import-Moduleを使用するにはSQL 2012が必要ですか?ちなみに「sqlps.exe」内で動作します- 詳細については、このスレッドを参照してください
Mister_Tom 2015

1
@Mister_Tom良い点。SQLPSモジュールはSQL 2012で導入されました。答えは、古いエディションにサンプルを適応させる方法を説明します。
Iain Samuel McLean Elder

1
@JasonMatney PowerShellはWindowsシステムの新しい管理インターフェイスですが、SQL Serverに関する多くのアドバイスが標準になる前に公開されていました。言葉を広める!:-)
Iain Samuel McLean Elder

1
この回答は、問題を処理するための有用な情報と強力で柔軟な代替方法を提供しますが、具体的に尋ねられた元の質問には完全に回答できません。私もパワーシェルファンですが、伝道をヒステリーに変えないでください。SQLCMDがすぐになくなるわけではありません。
2016

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

最後の行には、CSV固有のオプションが含まれています。

  • -W   個々のフィールドから末尾のスペースを削除する
  • -s","   列の区切り文字をコンマ(、)に設定します
  • -w 999   行幅を999文字に設定します

scottmの答えは私が使用するものに非常に近い-Wですが、それは本当に素晴らしい追加であることがわかります。CSVを別の場所で使用するときに空白を削除する必要はありません。

MSDN sqlcmdリファレンスも参照してください。それは/?オプションの出力を恥に置きます。


19
クエリ/入力ファイルの先頭にある@sims "set nocount on"
d -_- b

7
ヘッダーの下線を削除するにはどうすればよいですか?
ntombela 2010年

@gugulethun:クエリでユニオンを実行して、列名を最初の行に配置できます。
Nordes、2011

2
これは魅力のように機能しますが、列にセパレータが含まれていると、破損したcsvファイルが返されます...
Peter

受け入れられた回答にもこのコメントを追加しましたが、... '""' + col1 + '""' AS col1(二重)二重引用符で囲むか、ストアドプロシージャを呼び出すだけで、この問題を回避できます。
MisterIsaak 2013

59

これbcpは意図されていませんか?

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

コマンドラインからこれを実行して、構文を確認します。

bcp /?

例えば:

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]

bcp列ヘッダーは出力できませんのでご注意ください。

参照:bcpユーティリティのドキュメントページ。

上記のページの例:

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

1
ServerName = YourcomputerName \ SQLServerName、それ以外では実行されないエラー
Hammad Khan

1
bcp.exeの完全なドキュメントを表示したい場合: technet.microsoft.com/en-us/library/ms162802.aspx
Robert Bernstein

5
列名もヘッダーとしてエクスポートする必要がある場合はどうしますか?bcpを使用した簡単な汎用ソリューションはありますか?
Iain Samuel McLean Elder

@johndacostaどうもありがとう。列ヘッダーもどのように印刷しますか?簡単な切り替えはどこにもありません。ありがとう!
Rachael

1
bcpヘッダー(列名)は含まれていませんが、sqlcmd(私の経験から)10倍高速です。非常に大きなデータの場合は、を使用bcpしてデータを取得し、sqlcmd(select top 0 * from ...)を使用してヘッダーを取得し、それらを結合できます。
YJZ

12

これを行おうとしているが列ヘッダーもあるという人へのメモ、これは私がバッチファイルを使用したソリューションです。

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

これにより、最初の結果(ヘッダーとデータの間の----、----セパレーターを含む)が一時ファイルに出力され、findstrを介してフィルタリングすることにより、その行が削除されます。フィルターで除外されているため、完全ではないことに注意してください。-,-出力に列が1つしかない場合は機能せず、その文字列を含む正当な行も除外されます。


1
代わりに次のフィルターを使用してください。findstr / r / v ^ \-[、\-] * $> output.csv何らかの理由で、単純な^ [、\-] * $はすべての行に一致します。
ウラジミールコロレフ2011

3
^は常に二重引用符で囲み、正規表現を入れてください。^はcmd.exeのエスケープ文字であるため、奇妙な結果になります。上記の両方の正規表現は、私が知る限り正しく機能しませんが、これは機能します:findstr / r / v "^-[-、] *-。$"($の前の。は、エコー、ただしsqlcmd出力ではない場合があります)
JimG

日付には、日付と時刻の間に1つではなく2つのスペースがあるという問題もあります。ExcelでCSVを開こうとすると、00:00.0と表示されます。これを解決する簡単な方法は、SEDを使用してすべての ""をインプレースで検索および置換することです。スクリプトに追加するコマンドは、SED -i "s / / / g" output.csvです。SEDの詳細gnuwin32.sourceforge.net/packages/sed.htm
PollusB

これは正解です。@ JimGを追加すると完全に機能します。区切り記号にセミコロンを使用し、入力にSQLファイルを使用しました。このファイルには、noncount onおよびansi_warning offパーツが含まれ、スペースの削除には-Wスイッチが含まれています
robotik

1

この答えは、@ iain-elderのソリューションに基づいています。これは、大規模なデータベースの場合を除いてうまく機能します(彼のソリューションで指摘)。テーブル全体がシステムのメモリに収まる必要があり、私にとってこれはオプションではありませんでした。最善の解決策は、System.Data.SqlClient.SqlDataReaderとカスタムCSV シリアライザー例についてはこちらを参照)またはMS SQLドライバーとCSVシリアル化を備えた別の言語を使用することだと思います。おそらく依存関係のないソリューションを探していた最初の質問の精神で、以下のPowerShellコードが私のために機能しました。特に$ data配列をインスタンス化し、$ chunk_size行ごとに追加モードでExport-Csvを呼び出す場合は、非常に低速で非効率的です。

$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

BCPの代替オプション:

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

1

通常、デフォルトでCSVにエクスポートするユーティリティが(の一部として)sqlcmd付属しています。bcpmssql-tools

使用法:

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

例えば:

bcp.exe MyTable out data.csv

すべてのテーブルを対応するCSVファイルにダンプするには、次の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

上記の答えは私にとってそれをほぼ解決しましたが、解析されたCSVを正しく作成しません。

これが私のバージョンです:

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

sqlcmdいくつかのPowerShellの代わりに古くなっていると言っている人は、それsqlcmdがWindowsだけのものではないことを忘れています。私はLinuxを使用しています(Windowsの場合はとにかくPSを避けます)。

そうは言っても、私はbcpもっと簡単だと思います。


0

次の2つの理由から、CMDで私のソリューションを実行する必要があります。

  1. クエリに二重引用符が含まれている可能性があります
  2. リモートの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

ハックな方法でそれを行うことができます。sqlcmdハックを慎重に使用してください。データに二重引用符またはカンマが含まれていると、問題が発生します。

簡単なスクリプトを使用して、適切に実行できます。

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

ソース:VBScriptを使用してSQL出力をCSVに書き込む


1
反対票についての説明がいいでしょう。私の答えは正しいです。sqlcmdではこれを行うことはできません。このタスクを実行する別の方法も提供しました。
Sarel Botha 2012年

4
反対票は、OPがsqlcmdで求めていることを明確に実行できるためです。
ブライアンドリスコル

2
@BrianDriscoll、彼はでそれを行うことができないとは言わなかったsqlcmd、私たちはsqlcmdコンマを適切にエスケープしていないという事実を述べていただけなので、深刻な CSV出力にはほとんど使用できませんでした。
セバスチャン

私はそう言ったが、反対票に飽きたので、私は私の回答を編集した。編集内容をより忠実に説明します。
Sarel Botha 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.