Comment exporter des données au format CSV à partir de SQL Server avec sqlcmd?
-
06-07-2019 - |
Question
Je peux facilement transférer des données dans un fichier texte tel que:
sqlcmd -S myServer -d myDB -E -Q "select col1, col2, col3 from SomeTable"
-o "MyData.txt"
Cependant, j’ai consulté les fichiers d’aide de SQLCMD
mais je n’ai pas vu d’option spécifique pour CSV.
Y a-t-il un moyen de vider les données d'une table dans un fichier texte CSV en utilisant SQLCMD
?
La solution
Vous pouvez exécuter quelque chose comme ceci:
sqlcmd -S MyServer -d myDB -E -Q "select col1, col2, col3 from SomeTable"
-o "MyData.csv" -h-1 -s"," -w 700
-
-h-1
supprime les en-têtes de noms de colonnes du résultat
-
-s ","
"définit le séparateur de colonnes sur,
-
-w 700
définit la largeur de la ligne sur 700 caractères (elle devra être aussi large que la ligne la plus longue ou être renvoyée à la ligne suivante)
Autres conseils
sqlcmd -S myServer -d myDB -E -o "MyData.txt" ^
-Q "select bar from foo" ^
-W -w 999 -s","
La dernière ligne contient des options spécifiques à CSV.
-
-W
& nbsp; supprimer les espaces de fin de chaque champ individuel -
-s ", "
& nbsp; définit le séparateur de colonne sur la virgule (,) -
-w 999
& nbsp; définit la largeur de la ligne sur 999 caractères
scottm's answer est très proche de ce que j'utilise, mais je trouve le -W
très intéressant: je n'ai pas besoin de rogner les espaces lorsque je consomme le CSV ailleurs.
Voir également la référence MSDN sqlcmd . Cela fait honte à la sortie de l'option /?
.
N'est-ce pas que bcp
était destiné?
bcp "select col1, col2, col3 from database.schema.SomeTable" queryout "c:\MyData.txt" -c -t"," -r"\n" -S ServerName -T
Exécutez ceci à partir de votre ligne de commande pour vérifier la syntaxe.
bcp /?
Par exemple:
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]
Notez que bcp
ne peut pas générer les en-têtes de colonne.
Voir: Utilitaire bcp dans la page "Documents".
Exemple de la page ci-dessus:
bcp.exe MyTable out "D:\data.csv" -T -c -C 65001 -t , ...
Avec PowerShell, vous pouvez résoudre le problème en connectant 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 inclut le module SqlServer , qui contient la cmdlet Invoke-Sqlcmd
, que vous aurez même si vous installez simplement SSMS 2016. Avant cela, SQL Server 2012 incluait l'ancien module SQLPS . >, qui changerait le répertoire actuel en SQLSERVER: \
lors de la première utilisation du module (entre autres bogues), vous devrez donc changer le #Requires
ligne ci-dessus à:
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
Pour adapter l'exemple à SQL Server 2008 et 2008 R2, supprimez entièrement la ligne #Requires
et utilisez la utilitaire sqlps.exe à la place de l'hôte PowerShell standard.
Invoke-Sqlcmd est l'équivalent PowerShell de sqlcmd.exe. . Au lieu de texte, le résultat est System.Data.DataRow . objets.
Le paramètre -Query
fonctionne comme le paramètre -Q
de sqlcmd.exe. Transmettez-lui une requête SQL décrivant les données à exporter.
Le paramètre -Database
fonctionne comme le paramètre -d
de sqlcmd.exe. Donnez-lui le nom de la base de données contenant les données à exporter.
Le paramètre -Server
fonctionne comme le paramètre -S
de sqlcmd.exe. Donnez-lui le nom du serveur contenant les données à exporter.
Export-CSV est une applet de commande PowerShell qui sérialise des objets génériques. au format CSV. Il est livré avec PowerShell.
Le paramètre -NoTypeInformation
supprime les sorties supplémentaires qui ne font pas partie du format CSV. Par défaut, la cmdlet écrit un en-tête avec les informations de type. Il vous permet de connaître le type de l'objet lorsque vous le désérialisez ultérieurement avec Import-Csv
, mais cela confond les outils qui attendent un format CSV standard.
Le paramètre -Path
fonctionne comme le paramètre -o
de sqlcmd.exe. Un chemin complet pour cette valeur est le plus sûr si vous êtes bloqué avec l'ancien module SQLPS .
Le paramètre -Encoding
fonctionne comme les paramètres -f
ou -u
de sqlcmd.exe. Par défaut, Export-Csv génère uniquement des caractères ASCII et remplace tous les autres par des points d'interrogation. Utilisez plutôt UTF8 pour conserver tous les caractères et rester compatible avec la plupart des autres outils.
Le principal avantage de cette solution par rapport à sqlcmd.exe ou à bcp.exe est que vous n'avez pas à pirater la commande pour générer un fichier CSV valide. La cmdlet Export-Csv gère tout cela pour vous.
Le principal inconvénient est que Invoke-Sqlcmd
lit l'intégralité du jeu de résultats avant de le transmettre le long du pipeline. Assurez-vous de disposer de suffisamment de mémoire pour tout le jeu de résultats à exporter.
Cela risque de ne pas fonctionner correctement pour des milliards de lignes. Si cela pose un problème, vous pouvez essayer les autres outils ou lancer votre propre version efficace de Invoke-Sqlcmd
en utilisant Classe System.Data.SqlClient.SqlDataReader .
Une note pour ceux qui souhaitent faire cela mais qui ont aussi les en-têtes de colonne, voici la solution pour laquelle j'ai utilisé un fichier de commandes:
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
Ceci affiche les résultats initiaux (y compris les séparateurs ----, ---- entre les en-têtes et les données) dans un fichier temporaire, puis supprime cette ligne en la filtrant via findstr. Notez que ce n'est pas parfait car il filtre -, -
- il ne fonctionnera pas s'il n'y a qu'une seule colonne dans la sortie et il filtrera également les lignes légitimes contenant cette chaîne.
Autre option avec BCP:
exec master..xp_cmdshell 'BCP "sp_who" QUERYOUT C:\av\sp_who.txt -S MC0XENTC -T -c '
Cette réponse s'appuie sur la solution de @ iain-elder, qui fonctionne bien à l'exception du cas de la base de données volumineuse (comme indiqué dans sa solution). La table entière doit tenir dans la mémoire de votre système, et pour moi, ce n'était pas une option. Je pense que la meilleure solution utiliserait le System.Data. SqlClient.SqlDataReader et un sérialiseur CSV personnalisé ( voir ici pour un exemple ) ou une autre langue avec un MS Pilote SQL et sérialisation CSV. Dans l'esprit de la question initiale qui cherchait probablement une solution sans dépendance, le code PowerShell ci-dessous fonctionnait pour moi. Il est très lent et inefficace, en particulier pour instancier le tableau $ data et appeler Export-Csv en mode ajout pour chaque ligne $ 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 = Cette réponse s'appuie sur la solution de @ iain-elder, qui fonctionne bien à l'exception du cas de la base de données volumineuse (comme indiqué dans sa solution). La table entière doit tenir dans la mémoire de votre système, et pour moi, ce n'était pas une option. Je pense que la meilleure solution utiliserait le System.Data. SqlClient.SqlDataReader et un sérialiseur CSV personnalisé ( voir ici pour un exemple ) ou une autre langue avec un MS Pilote SQL et sérialisation CSV. Dans l'esprit de la question initiale qui cherchait probablement une solution sans dépendance, le code PowerShell ci-dessous fonctionnait pour moi. Il est très lent et inefficace, en particulier pour instancier le tableau $ data et appeler Export-Csv en mode ajout pour chaque ligne $ chunk_size.
<*>.Exception.Message
Write-Output $ErrorMessage
$read=$FALSE
$connection.Close()
exit
}
}
$connection.close()
Généralement, sqlcmd
est fourni avec Utilitaire> bcp
(faisant partie de mssql-tools
), qui exporte par défaut vers le format CSV.
Utilisation:
bcp {dbtable | query} {in | out | queryout | format} datafile
Par exemple:
bcp.exe MyTable out data.csv
Pour vider toutes les tables dans les fichiers CSV correspondants, voici le 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
Une réponse ci-dessus a presque résolu le problème pour moi, mais cela ne crée pas correctement un fichier CSV analysé.
Voici ma version:
sqlcmd -S myurl.com -d MyAzureDB -E -s, -W -i mytsql.sql | findstr /V /C:"-" /B > parsed_correctly.csv
Quelqu'un qui dit que sqlcmd
est obsolète au profit d'une alternative à PowerShell, oublie que sqlcmd
ne concerne pas que Windows. Je suis sous Linux (et sous Windows, j’évite malgré tout PS).
Cela dit, je trouve bcp
plus facile.
Depuis les 2 raisons suivantes, vous devez exécuter ma solution dans CMD:
- Il peut y avoir des guillemets doubles dans la requête
-
Nom d'utilisateur de connexion & amp; un mot de passe est parfois nécessaire pour interroger une instance SQL Server distante
sqlcmd -U [your_User] -P[your_password] -S [your_remote_Server] -d [your_databasename] -i "query.txt" -o "output.csv" -s"," -w 700
Vous pouvez le faire de manière furtive. Faites attention en utilisant le hack sqlcmd
. Si les données comportent des guillemets ou des virgules, vous rencontrerez des problèmes.
Vous pouvez utiliser un script simple pour le faire correctement:
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' 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!")
Source: Écriture de la sortie SQL au format CSV avec VBScript .