次のようなテキストファイルにデータを簡単にダンプできます。
sqlcmd -S myServer -d myDB -E -Q "select col1, col2, col3 from SomeTable"
-o "MyData.txt"
ただし、ヘルプファイルを確認しましSQLCMD
たが、CSV専用のオプションは確認していません。
を使用してテーブルからCSVテキストファイルにデータをダンプする方法はありますSQLCMD
か?
次のようなテキストファイルにデータを簡単にダンプできます。
sqlcmd -S myServer -d myDB -E -Q "select col1, col2, col3 from SomeTable"
-o "MyData.txt"
ただし、ヘルプファイルを確認しましSQLCMD
たが、CSV専用のオプションは確認していません。
を使用してテーブルからCSVテキストファイルにデータをダンプする方法はありますSQLCMD
か?
回答:
次のように実行できます。
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文字に設定します(これは最長の行と同じ幅にする必要があります。そうしないと、次の行に折り返されます)'""' + col1 + '""' AS col1
(二重の)二重引用符で囲むか、ストアドプロシージャを呼び出すだけで、この問題を回避できます。
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
か-u
sqlcmd.exeのパラメータ。デフォルトでは、Export-CsvはASCII文字のみを出力し、その他すべてを疑問符に置き換えます。代わりにUTF8を使用して、すべての文字を保持し、他のほとんどのツールとの互換性を維持してください。
sqlcmd.exeまたはbcp.exeに対するこのソリューションの主な利点は、有効なCSVを出力するためにコマンドをハックする必要がないことです。Export-Csvコマンドレットがすべてを処理します。
主な欠点はInvoke-Sqlcmd
、パイプラインに沿って渡す前に結果セット全体を読み取ることです。エクスポートする結果セット全体に対して十分なメモリがあることを確認してください。
数十億行ではスムーズに機能しない可能性があります。それが問題である場合は、他のツールを試すか、System.Data.SqlClient.SqlDataReaderクラスをInvoke-Sqlcmd
使用し て独自の効率的なバージョンをロールすることができます。
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リファレンスも参照してください。それは/?
オプションの出力を恥に置きます。
'""' + col1 + '""' AS col1
(二重)二重引用符で囲むか、ストアドプロシージャを呼び出すだけで、この問題を回避できます。
これ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 , ...
bcp
ヘッダー(列名)は含まれていませんが、sqlcmd
(私の経験から)10倍高速です。非常に大きなデータの場合は、を使用bcp
してデータを取得し、sqlcmd
(select top 0 * from ...)を使用してヘッダーを取得し、それらを結合できます。
これを行おうとしているが列ヘッダーもあるという人へのメモ、これは私がバッチファイルを使用したソリューションです。
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つしかない場合は機能せず、その文字列を含む正当な行も除外されます。
この答えは、@ 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()
通常、デフォルトでCSVにエクスポートするユーティリティが(の一部として)sqlcmd
付属しています。bcp
mssql-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
上記の答えは私にとってそれをほぼ解決しましたが、解析された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
もっと簡単だと思います。
ハックな方法でそれを行うことができます。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!")
sqlcmd
、私たちはsqlcmd
コンマを適切にエスケープしていないという事実を述べていただけなので、深刻な CSV出力にはほとんど使用できませんでした。