SQL Server的BULK INSERT能否从命名管道/有名管道读取数据?

9

是否可以使用BULK INSERT/bcp从命名管道fifo-style读取数据?

也就是说,除了从真实文本文件中读取外,是否可以让BULK INSERT/bcp从另一个进程的写端口读取命名管道中的数据?

例如:

  1. 创建命名管道
  2. 将文件解压缩到命名管道中
  3. 使用bcp或BULK INSERT从命名管道中读取数据

或者:

  1. 创建4个命名管道
  2. 将1个文件分成4个流,将每个流写入单独的命名管道中
  3. 使用bcp或BULK INSERT从4个命名管道读取数据到4个表中

我找到的最接近的是this fellow(网站现在无法访问),他使用自己的实用程序和以下用法成功地使用bcp向命名管道写入数据:

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

但他无法使反向操作起作用。

因此,在我进行一项愚蠢的任务之前,我想知道使用BULK INSERT或bcp从命名管道中读取是否从根本上可行。如果可能,如何设置?使用.NET System.IO.Pipes 命名空间中的NamedPipeServerStream或其他什么东西足够吗?

例如,使用PowerShell的示例

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

然后...呢?


当您将“out”替换为“in”时会发生什么?它应该可以工作... - Aaronaught
我自己没有尝试过,但那位同行报告说它没有起作用。 - Peter Radocchia
你可以使用SqlBulkCopy类进行编程实现(我将其作为注释发布,因为这只是一个提示)。 - momobo
1
是的,可以使用SqlBulkCopy进行编程实现,当然也可以在SSIS中完成。在类Unix系统上,mkfifo将在文件系统上创建一个“fifo”或命名管道,它看起来就像普通文件,然后您设置一个进程来写入它,另一个进程来读取它。然后通过fifo传输数据,永远不会在磁盘上实现。这是我试图复制的过程类型。 - Peter Radocchia
如果您正在尝试批量加载数据到服务器,并且不介意编写一个.NET程序集来完成它,为什么不直接使用ADO.NET中的批量插入功能呢?http://msdn.microsoft.com/en-us/library/7ek5da1a.aspx - Chris Smith
4个回答

5
我想评论一下 @DanMenes(感谢灵感),但为了参考,我将其作为单独的答案添加。
我在 .NET 中找到了一个解决方案,它打开了一个管道(实际上有两个,第一个像 @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. Accept connections

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项目获取更多详情。
注意:我还没有为该项目添加性能测试,但是初步测试显示,在事务性INSERTs方面,性能提升了2倍至5倍。

你知道为什么这个解决方案适用于批量插入而不适用于bcp吗? - Swapnil
当我将“\.\pipe\mypipe”替换为“\System.Environment.MachineName\pipe\mypipe”时,我收到以下错误: _System.Data.SqlClient.SqlException (0x80131904):批量加载:在数据文件中遇到意外的文件结尾_。您知道这个错误的原因以及如何修复它吗? - Swapnil
1
嗨...只是在玩代码。我们有一个20GB的文件被放入了一个3G的.tar.gz中。我试图看看是否可以将C# GZipStream钩到C# tar reader上,并通过您的实现使用_gzipStream.CopyTo(PipeStream)。我遇到了一些问题。首先是代码中的那个丢弃的命名管道。空数据值会导致在管道上写入1个空字节。结果证明这很重要。如果没有它,Sql端会立即抛出“不完整”的错误,并永远无法通过丢弃的部分。 - user1664043
1
其次,_gzipStream.CopyTo(PipeStream)在写入时立即出现“管道已断开”的错误。不幸的是,将一个20GB的文件吸入内存并使用20GB缓冲区进行单个写入似乎并不实际。必须在单个写入中执行整个操作似乎会限制可以使用此方法的用例。 - user1664043
1
我再次运行了我的示例,似乎在收到“断开的管道”错误之前已经向命名管道写入了约240K。Stream.CopyTo()以80K缓冲区块工作,因此在第4个缓冲区块写入之前,我已经写入了3个缓冲区块,然后出现了错误。 - user1664043
显示剩余4条评论

5

很遗憾,SSIS平面文件适配器、BULK INSERT和BCP都会对文件进行独占式写锁定(即使实际上并没有写入)。这就是为什么它不能正常工作的原因。

我不确定管道是否可以设置允许两个独占锁定在同一管道上而不需要进行一些严重的黑客攻击。你可以绕过它或者黑进fltmgr.sys :)

正如其他帖子中所建议的那样,使用.NET API进行批量处理或者使用OLEDB或ODBC接口可能更简单,尽管这意味着你需要编写自己的文件解析器。


5

我已成功将BULK INSERT(但不是BCP)与Windows 7和SQL Server 2008R2上的命名管道正确配合使用。这需要一些技巧。

首先,我必须在两个不同的线程上创建两个命名管道实例,两者都具有相同的管道名称。 SQL Server将打开第一个实例,在其中读取几个字节,然后关闭它,在第一个线程中引发PipeException异常。然后,SQL Server将立即重新打开命名管道,并从中流入所有数据。如果没有第二个位于后台且准备好提供数据的线程,则在我的第一个线程有时间从PipeException中恢复之前,SQL服务器会返回错误。

其次,我必须在单个WriteFile调用中写入所有数据。我开始使用循环向管道写入多个批次,但是BULK INSERT仅使用我写的第一批数据。它似乎执行了一次非阻塞读取,并将返回零字节的任何读取视为文件结束。

第三,如果使用XML格式文件,则必须将其写入常规文件。我没有成功使SQL Server从管道中读取格式文件。我不知道它是否可以从管道中读取非XML格式文件。


丹,这非常有趣,感谢您发布使其工作的步骤。很抱歉没有早些回复,我已经离开SO一段时间了,而且我自己也没有尝试过你的技术,但我不认为它不能用于例如将zip文件提取到命名管道并直接读入数据库。您在使用bcp时遇到了什么错误? - Peter Radocchia
如果我没记错的话,BCP会抱怨文件意外结束。根据我在网上阅读的内容,我认为正在发生的是BCP尝试对其输入文件执行seek()操作,如果输入是管道,则失败。 - Dan Menes
我可以确认这个方法是有效的,尽管我正在努力解决SQLServer过早截断管道的问题。它只能接受1024个字符。仍在调查这个限制的来源。 - Grimace of Despair

-1

BCP是否接受STDIN?如果是,您可以尝试直接通过管道传输,而无需创建命名管道...例如:

gunzip authors.txt.gz | bcp schema.tablename

2
难道你不认为如果这么简单,提问者就不会问这个问题了吗?所以,bcp 不接受 STDIN:https://connect.microsoft.com/SQLServer/feedback/details/489777/bcp-and-stdio - underscore_d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接