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 encore vu d’option spécifique pour CSV.
Existe-t-il un moyen de transférer les données d'une table dans un fichier texte CSV à l'aide de SQLCMD
?
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 sinon elle sera renvoyée à la ligne suivante)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
supprime les espaces de fin de chaque champ individuel-s","
définit le séparateur de colonne sur la virgule (,) -w 999
définit la largeur de la ligne à 999 caractèresLa réponse de scottm est très proche de ce que j'utilise, mais je trouve que le -W
est un ajout vraiment sympa: je n'ai pas besoin de couper les espaces lorsque je consomme le CSV ailleurs.
Voir également la référence MSDN sqlcmd . Il met la sortie de l'option /?
à honte.
N’est-ce pas bcp
?
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 d’en-têtes de colonne.
Voir: Utilitaire bcp page.
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 proprement 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 l'applet de commande Invoke-Sqlcmd
, que vous aurez même si vous venez d'installer SSMS 2016. Auparavant, SQL Server 2012 incluait l'ancien SQLPS. module , qui modifierait le répertoire actuel en SQLSERVER:\
lors de la première utilisation du module (entre autres bugs), vous devrez donc modifier la ligne #Requires
ci-dessus en:
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 l'utilitaire sqlps.exe à la place de l'hôte PowerShell standard.
Invoke-Sqlcmd est l'équivalent PowerShell de sqlcmd.exe. Au lieu du texte, il génère System.Data.DataRow objects.
Le paramètre -Query
fonctionne comme le paramètre -Q
de sqlcmd.exe. Transmettez-lui une requête SQL décrivant les données que vous souhaitez 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. Transmettez-lui le nom du serveur contenant les données à exporter.
Export-CSV est une applet de commande PowerShell qui sérialise les objets génériques en 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 fichier 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'ensemble des 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 que vous souhaitez 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 System.Data.SqlClient.SqlDataReader class.
Une note pour ceux qui cherchent à faire cela mais qui ont aussi les en-têtes de colonne, c’est la solution pour laquelle j’ai utilisé un fichier 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
Ceci sort 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, sauf dans le cas d'une 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 soupçonne que la meilleure solution utiliserait System.Data.SqlClient.SqlDataReader et un sérialiseur CSV personnalisé ( voir ici pour un exemple ) ou un autre langage avec un pilote MS SQL et une sérialisation CSV. Dans l'esprit de la question initiale qui cherchait probablement une solution sans dépendance, le code PowerShell ci-dessous a fonctionné pour moi. C'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 = $_.Exception.Message
Write-Output $ErrorMessage
$read=$FALSE
$connection.Close()
exit
}
}
$connection.close()
Depuis 2 raisons suivantes, vous devriez exécuter ma solution dans CMD:
Le nom d'utilisateur et le mot de passe de connexion sont parfois nécessaires 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
sqlcmd
est généralement fourni avec bcp
utility (dans le cadre de mssql-tools
) qui exporte au format CSV par défaut.
Usage:
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 affirmant que sqlcmd
est obsolète au profit d'une alternative à PowerShell, c'est oublier que sqlcmd
n'est pas réservé à Windows. Je suis sous Linux (et sous Windows, j'évite malgré tout PS).
Cela dit, je trouve bcp
plus facile.