Может ли Sql Server BULK INSERT читать с именованного канала / fifo? - PullRequest
7 голосов
/ 04 февраля 2010

Возможно ли BULK INSERT / bcp читать из именованного канала в стиле fifo ?

То есть вместо чтения из реального текстового файла можно сделать BULK INSERT / bcp для чтения из именованного канала, который находится в конце записи другого процесса?

Например:

  1. создать именованный канал
  2. распаковать файл в именованный канал
  3. чтение из именованного канала с помощью bcp или BULK INSERT

или

  1. создать 4 именованных канала
  2. разбить 1 файл на 4 потока, записав каждый поток в отдельный именованный канал
  3. чтение из 4 именованных каналов в 4 таблицы с BCP или BULK INSERT

Самым близким, что я нашел, был этот парень (сайт теперь недоступен), которому удалось написать в именованный канал с bcp, со своей собственной утилитой и использованием, подобным так:

start /MIN ZipPipe authors_pipe authors.txt.gz 9
bcp  pubs..authors out  \\.\pipe\authors_pipe -T -n

Но он не мог заставить работать наоборот.

Поэтому, прежде чем я отправлюсь по дурацкому поручению, я задаюсь вопросом, возможно ли принципиально прочитать из именованного канала с BULK INSERT или bcp. И если это возможно, как его настроить? NamedPipeServerStream или что-то еще в пространстве имен .NET System.IO.Pipes будет адекватным?

например, пример с использованием Powershell:

[reflection.Assembly]::LoadWithPartialName("system.core")
$pipe = New-Object system.IO.Pipes.NamedPipeServerStream("Bob")

А потом .... что?

Ответы [ 4 ]

5 голосов
/ 18 ноября 2013

Я бы прокомментировал @DanMenes (спасибо за вдохновение), но для справки я добавляю его как отдельный ответ.

Я разработал решение в .NET , которое открывает канал (фактически 2, первый разрушается, как сказал @DanMenes), подготавливает поток данных к нему и затем запускает BULK INSERT с автоматически сгенерированным файлом формата.

Предпосылка в том, что я могу делать такие вещи, как

  var inMemoryData = new[] {
    new[] { "val1", "val2" },
    new[] { "val3", "val4" },
  };

  using (var importer = new Importer(SqlConnection, "MyTable", "Col1", "Col2"))
  {
    importer.Import(inMemoryData);
  }

Я подведу итог реализации Importer:

1. Создать трубу

var stream = new NamedPipeServerStream(name, PipeDirection.Out, 2, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
stream.BeginWaitForConnection(OnConnection, this);

2. Принимаю соединения

public void OnConnection(IAsyncResult asyncResult)
{
  Stream.EndWaitForConnection(asyncResult);

  var buffer = Encoding.UTF8.GetBytes(data);
  Stream.Write(buffer, 0, buffer.Length);
  Stream.Close();
}

3. Начать BULK INSERT

var insertCommand = DbConnection.CreateCommand();
insertCommand.CommandText = "BULK INSERT [MyTable] FROM '\\.\pipe\mypipe' WITH (FORMATFILE='c:\path\to\formatfile')";
insertCommand.ExecuteNonQuery();

Подробнее см. проект GitHub .

Примечание. Я еще не добавил тесты производительности в проект, но предварительные тесты показали увеличение производительности в 2–5 раз по сравнению с транзакциями INSERTs.

5 голосов
/ 08 июля 2013

Мне удалось заставить BULK INSERT (но не BCP) корректно работать с именованными каналами в Windows 7 и SQL Server 2008R2. Есть несколько хитростей.

Сначала я должен был создать два именованных экземпляра канала в двух разных потоках, оба с одним и тем же именем канала. SQL Server откроет первый экземпляр, прочитает из него несколько байтов и закроет его, в результате чего WriteFile вызовет исключение PipeException в первом потоке. Затем SQL Server немедленно повторно открывает именованный канал и передает все данные из него. Если бы у меня не было второго потока, работающего в фоновом режиме, готового обработать данные, сервер SQL вернул бы ошибку, прежде чем мой первый поток успел восстановиться после PipeException.

Во-вторых, мне пришлось записать все данные за один вызов WriteFile. Я начал с цикла, в котором я написал несколько пакетов в канал, но BULK INSERT использовал только первый пакет, который я написал. Кажется, что он выполняет неблокирующее чтение и обрабатывает любое чтение, которое возвращает нулевые байты, как конец файла.

В-третьих, файл формата XML, если он используется, должен быть записан в обычный файл. Мне не удалось получить SQL Server для чтения файла формата из канала. Я не знаю, может ли он прочитать файл в формате, отличном от XML, из канала.

4 голосов
/ 15 июня 2012

К сожалению, оба адаптера плоских файлов служб SSIS, BULK INSERT и BCP, устанавливают эксклюзивную блокировку записи в файл (даже при том, что он фактически не записывает в него). Вот почему это не работает.

Я не уверен, что трубы могут быть настроены так, чтобы разрешить два эксклюзивных замка на одной трубе без какого-либо серьезного взлома. Вы можете обойти это, я полагаю, или взломать fltmgr.sys:)

Как предлагали другие авторы, использование .NET API для массовых операций или интерфейс OLEDB или ODBC, вероятно, проще, хотя это означает, что вам нужно написать собственный анализатор файлов.

0 голосов
/ 12 февраля 2013

BCP принимает STDIN? Если это так, вы можете попробовать просто передать его напрямую без создания именованного канала ... например:

gunzip authors.txt.gz | bcp schema.tablename
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...