web-dev-qa-db-fra.com

Comment exporter des données au format CSV à partir de SQL Server avec sqlcmd?

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?

112
Ray Vega

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)
117
scottm
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ères

La 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.

62
ESV

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 , ...
58
John DaCosta

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.

10
Rudism

Autre option avec BCP:

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

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()
0
jeffmax

Depuis 2 raisons suivantes, vous devriez exécuter ma solution dans CMD:

  1. Il peut y avoir des guillemets doubles dans la requête
  2. 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
    
0
Mohsen Abasi

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
0
kenorb

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.

0
Hack-R