Как экспортировать данные в формате CSV из SQL Server с помощью sqlcmd? - PullRequest
114 голосов
/ 08 января 2009

Я могу довольно легко вывести данные в текстовый файл, такой как:

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

Тем не менее, я просмотрел файлы справки для SQLCMD, но не видел ни одной опции специально для CSV.

Есть ли способ выгрузить данные из таблицы в текстовый файл CSV, используя SQLCMD?

Ответы [ 11 ]

121 голосов
/ 08 января 2009

Вы можете запустить что-то вроде этого:

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 символов (она должна быть такой же ширины, как самая длинная строка, иначе она будет перенесена на следующую строку)
63 голосов
/ 11 марта 2010
sqlcmd -S myServer -d myDB -E -o "MyData.txt" ^
    -Q "select bar from foo" ^
    -W -w 999 -s","

Последняя строка содержит специфичные для CSV опции.

  • -W удалить пробелы в каждом отдельном поле
  • -s"," устанавливает разделитель столбцов на запятую (,)
  • -w 999 устанавливает ширину строки в 999 символов

ответ Скотта очень близок к тому, что я использую, но я считаю -W действительно хорошим дополнением: мне не нужно обрезать пробел, когда я использую CSV в другом месте.

См. Также ссылку MSDN sqlcmd . Это ставит вывод опции /? в позор.

59 голосов
/ 25 марта 2011

Разве это не 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 , ...
55 голосов
/ 01 июня 2014

С 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 module , который изменит текущий каталог на 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 и используйте утилиту sqlps.exe вместо стандартного хоста PowerShell.

Invoke-Sqlcmd является эквивалентом PowerShell для sqlcmd.exe. Вместо текста выводится System.Data.DataRow объектов.

Параметр -Query работает подобно параметру -Q в sqlcmd.exe. Передайте ему запрос SQL, описывающий данные, которые вы хотите экспортировать.

Параметр -Database работает подобно параметру -d в sqlcmd.exe. Передайте ему имя базы данных, которая содержит данные для экспорта.

Параметр -Server работает подобно параметру -S в sqlcmd.exe. Передайте ему имя сервера, который содержит данные для экспорта.

Export-CSV - это командлет PowerShell, который сериализует общие объекты в CSV. Поставляется с 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 читает весь набор результатов перед передачей его по конвейеру. Убедитесь, что у вас достаточно памяти для всего набора результатов, который вы хотите экспортировать.

Может не работать гладко для миллиардов строк. Если это проблема, вы можете попробовать другие инструменты или развернуть собственную эффективную версию Invoke-Sqlcmd, используя System.Data.SqlClient.SqlDataReader class.

11 голосов
/ 31 августа 2011

Примечание для тех, кто хочет сделать это, но также имеет заголовки столбцов. Это решение, в котором я использовал пакетный файл:

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 голос
/ 16 ноября 2016

Альтернативный вариант с ППГ:

exec master..xp_cmdshell 'BCP "sp_who" QUERYOUT C:\av\sp_who.txt -S MC0XENTC -T -c '
0 голосов
/ 20 декабря 2018

Поскольку по двум причинам вы должны запустить мое решение в 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
    
0 голосов
/ 30 марта 2018

Ответ выше почти решил это для меня, но это не правильно создает проанализированный 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 голосов
/ 25 февраля 2018

Обычно sqlcmd поставляется с bcp утилитой (как часть mssql-tools), которая по умолчанию экспортируется в CSV.

Использование:

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 голосов
/ 28 июня 2017

Этот ответ основан на решении от @ iain-elder, которое хорошо работает, за исключением случая с большой базой данных (как указано в его решении). Вся таблица должна уместиться в памяти вашей системы, и для меня это не вариант. Я подозреваю, что лучшее решение будет использовать System.Data.SqlClient.SqlDataReader и собственный сериализатор CSV ( см. Здесь для примера ) или другой язык с драйвером MS SQL и сериализацией CSV , В духе первоначального вопроса, который, вероятно, искал решения без зависимости, приведенный ниже код PowerShell работал для меня. Это очень медленно и неэффективно, особенно в создании экземпляра массива $ data и вызове Export-Csv в режиме добавления для каждой строки $ 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...