2010-10-29 118 views
18

我有存储在一个表中的MS SQL Server 2005中有没有人有SQL代码,需要查询作为输入(可以说查询保证返回VARBINARY的单个列一些varbinary数据)并将这些字节输出到磁盘(每行一个文件?)我确信这个问题以前已经有过一千次的问题,但谷歌搜索主要提供了.net解决方案。我想要一个SQL解决方案。脚本varbinary数据保存到磁盘

回答

27

BCP的做法并不为我工作。它写入磁盘的字节不能被反序列化回我存储的.net对象。这意味着磁盘上的字节与存储的字节不同。也许BCP正在写某种标题。我不确定。

我发现下面的代码here在文章底部。它很棒!虽然它是用于存储BMP图像的,但它适用于任何varbinary。

DECLARE @SQLIMG VARCHAR(MAX), 
    @IMG_PATH VARBINARY(MAX), 
    @TIMESTAMP VARCHAR(MAX), 
    @ObjectToken INT 

DECLARE IMGPATH CURSOR FAST_FORWARD FOR 
     SELECT csl_CompanyLogo from mlm_CSCompanySettingsLocalizations 

OPEN IMGPATH 

FETCH NEXT FROM IMGPATH INTO @IMG_PATH 

WHILE @@FETCH_STATUS = 0 
    BEGIN 
     SET @TIMESTAMP = 'd:\' + replace(replace(replace(replace(convert(varchar,getdate(),121),'-',''),':',''),'.',''),' ','') + '.bmp' 

     PRINT @TIMESTAMP 
     PRINT @SQLIMG 

     EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT 
     EXEC sp_OASetProperty @ObjectToken, 'Type', 1 
     EXEC sp_OAMethod @ObjectToken, 'Open' 
     EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @IMG_PATH 
     EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, @TIMESTAMP, 2 
     EXEC sp_OAMethod @ObjectToken, 'Close' 
     EXEC sp_OADestroy @ObjectToken 

     FETCH NEXT FROM IMGPATH INTO @IMG_PATH 
    END 

CLOSE IMGPATH 
DEALLOCATE IMGPATH 
+2

运行这个可能需要启用OLE自动化程序https://msdn.microsoft.com/en-us/library/ms191188.aspx。 – 2016-05-05 02:56:31

11

您可以使用BCP,不是T-SQL,但效果很好。

BCP "SELECT FileContent FROM table WHERE ID = 1" queryout "C:\file.txt" -T 
+1

嗨达斯汀 - 我能够使用命令输出文件,但我不认为它的工作正常。数据是一个序列化的.net对象。我知道数据是正确存储的,因为我的进程使用.net的数据进行操作。但是,当我尝试反序列化时,我得到一个错误,这意味着字节写入不正确。思考?如果命令输出单个varbinary(max)值,是写入磁盘的实际字节还是包含头文件等? – SFun28 2010-10-30 15:12:54

+1

使用-N选项似乎可以正常工作 – 2013-12-19 03:26:17

+3

提示时使用以下选项: - 输入字段XXX [varbinary(max)]的文件存储类型: - 输入字段XXX的前缀长度[8 ]:0 - 输入领域XXX [0]的长度: - 输入字段终止[无]: - 你想保存在一个文件格式的信息? [Y/n] n breez 2015-10-15 10:33:08

0

SQL旨在与数据库对象一起工作,因此从其角度来看,其他任何东西都不存在。当然,像xp_cmdshell这样的扩展程序允许您与操作系统进行交互,但它们是专有扩展,而不是T-SQL的一部分。

也许最接近将使用二进制类型的SQL Server 2008中的,允许直接存储一些列的文件的文件夹中,而不是使用数据库FILESTREAM属性:

FILESTREAM overview

注意:的FILESTREAM存储装置进行以提高性能保持大文件从数据库中,而不是允许直接访问文件(即T-SQL仍然没有一个文件系统)的概念设计。我认为,从SQL直接访问文件系统将会破坏数据库的一些目的(主要是以结构化方式存储数据)。

因此,我建议以下达斯汀的建议,使用像BCP或任何其他数据自卸车的工具。

3

我知道这是旧的文章,但我想通了,为什么下面不工作,如何解决它:

BCP "SELECT FileContent FROM table WHERE ID = 1" queryout "C:\file.JPG" -T -N 

原因是BCP把前缀长度在一开始文件。它是4字节或8字节,取决于FileContent列的数据类型(文本,ntext,图像:4 varchar(max),varbinary(max):8请参考https://msdn.microsoft.com/en-us/library/ms190779.aspx

使用二进制编辑器一个在Visual Studio中,用于删除前缀字节,并且所有内容都完美运行。 :-)

1

如果您linqpad,这个工程:

void Main() 
{ 
    var context = this; 
    var query = 
     from ci in context.Images 
     where ci.ImageId == 10 
     select ci.Image 
    ; 
    var result = query.Single(); 
    var bytes = Convert.FromBase64String(result); 
    File.WriteAllBytes(@"c:\image.bmp", bytes); 
} 
+0

Pitch Perfect .. – irfandar 2016-03-03 11:27:18

+2

This works ...?真的,所以'var context = this'会自动搜索你的sql server实例,将会登录,会找到正确的数据库,其中有一个图表存在的方案...从来不知道LinqPad是这个聪明的! – 2016-04-12 14:23:21

+0

你有没有用过它?它并非如此。我猜想,我只是在想象它。 – JohnOpincar 2016-04-14 15:20:09

2

只是一个替代品。您可以使用免费软件Toad for sql server并直接从编辑器保存。

enter image description here

3

我加入这个建立在JohnOpincar的回答,让别人谁想要使用LinqPad可以得到一个有效的解决方案更快。

/* 
This LinqPad script saves data stored in a VARBINARY field to the specified folder. 
1. Connect to SQL server and select the correct database in the connection dropdown (top right) 
2. Change the Language to C# Program 
3. Change "Attachments" to the name of your table that holds the VARBINARY data 
4. Change "AttachmentBuffer" to the name of the field that holds the data 
5. Change "Id" to the unique identifier field name 
6. Change "1090" to the identity of the record you want to save 
7. Change the path to where you want to save the file. Make sure you choose the right extension. 

Notes: Windows 10 may give you "Access Denied" error when trying to save directly to C:\. Rather save to a subfolder. 
*/ 

void Main() 
{ 
    var context = this; 
    var query = 
     from ci in context.Attachments 
     where ci.Id == 1090 
     select ci.AttachmentBuffer 
    ; 
    byte[] result = query.Single().ToArray(); 
    File.WriteAllBytes(@"c:\DEV\dumpfile.xlsx", result); 
    Console.WriteLine("Done"); 
}