Может ли SQL Server BULK INSERT считываться из именованного канала/fifo?


Возможно ли для BULK INSERT / bcp читать из именованного канала, FIFO -style?

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

Например:

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

Или:

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

Ближе всего я нашел этого парня (сайт теперь недоступен), который сумел написать в именованную трубу w/ bcp, с его собственной утилитой и использованием так:

start /MIN ZipPipe authors_pipe authors.txt.gz 9
bcp  pubs..authors out  \.pipeauthors_pipe -T -n
Но он не мог заставить работать обратное. Поэтому, прежде чем я отправлюсь на дурацкое задание, я задаюсь вопросом, возможно ли вообще читать из именованной трубы с объемной вставкой или bcp. И если бы это было возможно, как бы вы это устроили? Будет ли NamedPipeServerStream или что-то еще в пространстве имен .NET System.IO.Pipes адекватным?

Например, пример С помощью Powershell:

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

И затем....Что?

4 5

4 ответа:

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

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

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

Я бы прокомментировал @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);
  }

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

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. Начать массовую вставку

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

Смотрите проект GitHub для получения более подробной информации.

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

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

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

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

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

gunzip authors.txt.gz | bcp schema.tablename