Як експортувати дані у формат 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
Bernhard Hofmann

Це не повинно бути, але я хотів знати напевно, чи може sqlcmd насправді зробити це, перш ніж зануритися в іншу експортну утиліту. Одне, що потрібно згадати, - це те, що він дійсно потребує написання сценарію.
Рей

Існує інструмент addin для 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 символів (це потрібно, щоб він був таким же широким, як і найдовший рядок, або він переходитиме до наступного рядка)

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 .


5
Інші відповіді чесно смокчуть, це єдиний спосіб зробити це правильно. Хочеться, щоб це було більш очевидним.
Shagglez

1
У SQL 2008 R2 мені довелося запустити інструмент "sqlps.exe", щоб використовувати Invoke-Sqlcmd. Мабуть, мені потрібен SQL 2012, щоб використовувати імпорт-модуль? У будь-якому випадку він працює в межах "sqlps.exe" - детальну інформацію див. У цій темі .
Mister_Tom

1
@Mister_Tom хороший момент. Модуль SQLPS був представлений з SQL 2012. Відповідь тепер пояснює, як адаптувати приклад для старих видань.
Ієн Самуель Маклін Старший

1
@JasonMatney PowerShell - це новий адміністративний інтерфейс для систем Windows, але багато порад SQL Server було опубліковано ще до того, як він став стандартним. Поширювати Слово! :-)
Ієн Семюел Маклін Старший

1
Ця відповідь надає корисну інформацію та потужний і гнучкий АЛЬТЕРНАТИВНИЙ спосіб вирішення проблеми, однак він не відповідає на початкове запитання, як було конкретно задано. Я теж шанувальник повноважень, але не давайте нехай євангелізм перетворюється на істерику. 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 знаків

Відповідь Scottm дуже близька до того, що я використовую, але я вважаю, що -Wце дуже приємне доповнення: мені не потрібно обробляти пробіли, коли я споживаю CSV в іншому місці.

Також див . Посилання на MSDN sqlcmd . Це приводить /?до ганьби вихід опції.


19
@sims "встановити рахунок" на початку запиту / вхідного файлу
d -_- b

7
Як я можу видалити підкреслення на заголовках?
ntombela

@gugulethun: Ви можете зробити об'єднання у своєму запиті, щоб поставити ім'я стовпця в перший рядок.
Nordes

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

Приклад із наведеної сторінки:

bcp.exe MyTable out "D:\data.csv" -T -c -C 65001 -t , ...

1
ServerName = YourcomputerName \ SQLServerName, лише тоді він виконує помилку в іншому випадку
Хаммад Хан

1
У разі , якщо ви хочете , щоб побачити повну документацію для Bcp.exe: technet.microsoft.com/en-us/library/ms162802.aspx
Роберт Бернштейн

5
Що ви робите, якщо вам також потрібно експортувати імена стовпців як заголовка? Чи є пряме загальне рішення за допомогою bcp?
Ієн Самуель Маклін Старший

@johndacosta велике спасибі Як би ви надрукували також заголовки стовпців? Я ніде не бачу легкого переходу на нього. Дякую!
Рахаель

1
bcpне включає заголовок (назви стовпців), але це на 10 разів швидше, ніж sqlcmd(з мого досвіду). Для дійсно великих даних ви можете використовувати bcpдля отримання даних і використовувати sqlcmd(виберіть верхній 0 * від ...), щоб отримати заголовок, а потім об'єднати їх.
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. Зауважте, що він не є ідеальним, оскільки він фільтрує -,-- він не працюватиме, якщо у виході є лише один стовпець, і він також відфільтрує законні рядки, що містять цей рядок.


1
Замість цього використовуйте наступний фільтр: findstr / r / v ^ \ - [, \ -] * $> output.csv Чомусь simle ^ [, \ -] * $ відповідає всім рядкам.
Володимир Корольов

3
Завжди поставте регулярний вираз з ^ всередині подвійних лапок, інакше ви отримаєте дивні результати, оскільки ^ - символ втечі для 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 для введення, у файлі містилася безрахунок на та ansi_warning off частина та перемикач -W для видалення місця
robotik

1

Ця відповідь ґрунтується на рішенні від @ iain -lder, яке добре працює, за винятком великого випадку бази даних (як зазначено в його рішенні). Вся таблиця повинна вміститись у пам’яті вашої системи, і для мене це не було варіантом. Я підозрюю, що найкращим рішенням буде використовувати System.Data.SqlClient.SqlDataReader та користувацький серіалізатор CSV ( див. Тут приклад ) або іншу мову з MS SQL-драйвером та серіалізацією CSV. В дусі оригінального запитання, яке, мабуть, шукало рішення не залежностей, код PowerShell нижче працював для мене. Це дуже повільно та неефективно, особливо при створенні масиву даних $ та виклику 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()

1

Альтернативний варіант з BCP:

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

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

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легше.


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
Основна причина полягає в тому, що ви чітко МОЖЕТЕ робити те, що просить ОП, з sqlcmd.
Брайан Дрісколл

2
@BrianDriscoll, Він не сказав , що один не може зробити це з sqlcmd, ми просто констатуємо факт , що sqlcmdнеправильно випливає коми, таким чином , будучи ледь використовувати для будь-якого серйозного виходу CSV.
Себастьян

Я це сказав, але втомився від низовин, тому я відредагував свою відповідь. Я зроблю свою редакцію більш правдивою.
Сарел Бота
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.