Как экспортировать данные в формате CSV из SQL Server с помощью sqlcmd?

136

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

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

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

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

луч
источник
Это должно быть сделано через sqlcmd, или вы могли бы использовать другую программу, такую ​​как следующая: codeproject.com/KB/aspnet/ImportExportCSV.aspx
Бернхард Хофманн
Это не должно быть, но я хотел знать наверняка, может ли sqlcmd действительно сделать это, прежде чем погрузиться в какую-то другую утилиту экспорта. Стоит упомянуть, что он должен быть сценарием.
Рэй
Существует инструмент надстройки SSMS 2008, который выводит CSV-данные из ваших таблиц, которые можно настраивать в зависимости от того, где и в каком порядке. store.nmally.com/software/sql-server-management-studio-addons/...

Ответы:

140

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

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 символов (она должна быть такой же ширины, как самая длинная строка, иначе она будет перенесена на следующую строку)
scottm
источник
22
Предостережение в том, что если вы сделаете это, ваши данные могут не содержать запятых.
Сарел Бота,
1
@SarelBotha, вы можете обойти эту проблему '""' + col1 + '""' AS col1, заключив в двойные кавычки или просто вызвав хранимую процедуру.
MisterIsaak
2
@JIsaak Затем убедитесь, что в ваших данных нет двойных кавычек, или замените их двойными.
Сарел Бота,
1
Может ли кто-нибудь уточнить, что нужно сделать, чтобы в данных были запятые? Нужно ли окружать каждый столбец символом "" "+ + ___ +" ""?
Ахмед
2
Этот ответ сейчас устарел. Сценарии PowerShell более гибкие и могут запускаться на SQL Server в качестве агента заданий.
Клинтон Уорд
75

С 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строку и используйте утилиту 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 класса.

Iain Samuel McLean Elder
источник
5
Другие ответы, честно говоря, отстой, это единственный способ сделать это правильно. Хотелось бы, чтобы это было более очевидно.
Шагглз
1
В SQL 2008 R2 мне пришлось запустить средство «sqlps.exe», чтобы использовать Invoke-Sqlcmd. Видимо мне нужен SQL 2012 для использования Import-Module? В любом случае, он работает в "sqlps.exe" - подробности смотрите в этой теме .
Mister_Tom
1
@Mister_Tom хороший момент. Модуль SQLPS был представлен в SQL 2012. Теперь в ответе объясняется, как адаптировать пример для более старых выпусков.
Иэн Сэмюэль Маклин, старейшина
1
@JasonMatney PowerShell - это новый административный интерфейс для систем Windows, но многие рекомендации по SQL Server были опубликованы до того, как стали стандартными. Распространить слово! :-)
Иэн Сэмюэл Маклин Старейшина
1
Этот ответ предоставляет полезную информацию и мощный и гибкий АЛЬТЕРНАТИВНЫЙ способ решения проблемы, однако он полностью не в состоянии ответить на первоначальный вопрос, как он был специально задан. Я тоже фанат PowerShell, но давайте не будем превращать евангелизацию в истерию. SQLCMD не уйдет в ближайшее время.
барбекю
69
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 . Это ставит /?вывод опциона в стыд.

ESV
источник
19
@sims "установить nocount on" в начале запроса / файла ввода
d -_- b
7
Как я могу удалить подчеркивание на заголовках?
Нтомбела
@gugulethun: Вы можете сделать объединение в своем запросе, чтобы поместить имя столбца в первую строку.
Nordés
2
Это работает как талисман, но если ваш столбец содержит разделитель, я получаю поврежденный CSV-файл ...
Питер
Этот комментарий также добавлен к принятому ответу, но ... вы можете обойти эту проблему '""' + col1 + '""' AS col1, обернув (удвоив) двойные кавычки или просто вызвав хранимую процедуру.
MisterIsaak
59

Разве это не 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 , ...
johndacostaa
источник
1
Имя_сервера = имя_компьютера \ имя_сервера SQLServerName, только тогда оно выполнит ошибку в противном случае
Хаммад Хан
1
Если вы хотите просмотреть полную документацию по bcp.exe: technet.microsoft.com/en-us/library/ms162802.aspx
Роберт Бернштейн,
5
Что делать, если вам нужно экспортировать имена столбцов в качестве заголовка? Есть ли прямое общее решение, использующее bcp?
Иэн Самуэль Маклин, старейшина
@johndacosta большое спасибо. Как бы вы напечатали заголовки столбцов? Я не вижу легкого переключения на это нигде. Спасибо!
Рэйчел
1
bcpне включает заголовок (имена столбцов), но он примерно в 10 раз быстрее sqlcmd(по моему опыту). Для действительно больших данных вы можете использовать их bcpдля получения данных и использовать sqlcmd(выберите top 0 * from ...), чтобы получить заголовок, а затем объединить их.
YJZ
12

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

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. Обратите внимание, что он не идеален, поскольку отфильтровывает -,-- он не будет работать, если в выводе есть только один столбец, а также отфильтровывает допустимые строки, содержащие эту строку.

Rudism
источник
1
Вместо этого используйте следующий фильтр: findstr / r / v ^ \ - [, \ -] * $> output.csv По какой-то причине simle ^ [, \ -] * $ соответствует всем строкам.
Владимир Королев
3
Всегда ставьте регулярное выражение с ^ в двойных кавычках, иначе вы получите странные результаты, потому что ^ является escape-символом для cmd.exe. Насколько я могу судить, оба приведенных выше регулярных выражения не работают должным образом, но это так: findstr / r / v "^ - [-,] * -. $" (. $ Перед тем, как $ понадобится при тестировании с эхо, но не для вывода sqlcmd)
JimG
Также существует проблема с датами, имеющими 2 пробела между датой и временем вместо одного. Когда вы пытаетесь открыть CSV в Excel, он отображается как 00: 00.0. Простой способ решить эту проблему - найти и заменить все "" на "" на месте с помощью SED. Команда для добавления в ваш скрипт: SED -i "s / / / g" output.csv. Подробнее о SED gnuwin32.sourceforge.net/packages/sed.htm
PollusB,
это правильный ответ, прекрасно работает с добавлением @ JimG. Я использовал точку с запятой для разделителя и файл sql для ввода, файл содержал часть noncount on и ansi_warning off и ключ -W для удаления пространства
robotik
1

Этот ответ основан на решении @ 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()
jeffmax
источник
1

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

exec master..xp_cmdshell 'BCP "sp_who" QUERYOUT C:\av\sp_who.txt -S MC0XENTC -T -c '
arnav
источник
1

Обычно 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
kenorb
источник
0

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

Hack-R,
источник
0

Поскольку по двум причинам вы должны запустить мое решение в 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
Мохсен Абаси
источник
-2

Вы можете сделать это хакерским способом. Осторожно, используя 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!")

Источник: Запись вывода SQL в CSV с VBScript .

Сарел Бота
источник
1
Объяснение понижения было бы неплохо. Мой ответ правильный: вы не можете сделать это с помощью sqlcmd. Я также предложил альтернативный способ выполнить задачу.
Сарел Бота
4
Недостаток в том, что вы явно МОЖЕТЕ делать то, что просит OP, с помощью sqlcmd.
Брайан Дрисколл
2
@BrianDriscoll, Он не сказал , что один не может сделать это с sqlcmd, мы просто констатируем факт , что sqlcmdне правильно вытекающую запятой, таким образом , будучи едва использовать для любого серьезного выхода CSV.
Себастьян
Я сказал это, но устал от отрицательных голосов, поэтому я отредактировал свой ответ. Я сделаю мою правду более правдивой.
Сарел Бота