我有一些 varbinary 数据存储在 MS Sql Server 2005 的表中。有没有人有将查询作为输入的 SQL 代码(假设查询保证返回单列 varbinary)并将字节输出到磁盘(一个文件每行?)我敢肯定这已经被问过一千次了,但谷歌搜索主要是.net解决方案。我想要一个 SQL 解决方案。
8 回答
BCP 方法对我不起作用。它写入磁盘的字节不能反序列化回我存储的 .net 对象。这意味着磁盘上的字节不等于存储的内容。也许 BCP 正在编写某种标题。我不知道。
我在文章底部找到了以下代码。效果很好!虽然它适用于存储的 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
我在JohnOpincar's answer 的基础上添加了这个,以便其他想要使用 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");
}
您可以使用 BCP,而不是 T-SQL,但效果很好。
BCP "SELECT FileContent FROM table WHERE ID = 1" queryout "C:\file.txt" -T
我知道这是一篇旧帖子,但我知道为什么以下内容不起作用以及如何解决它:
BCP "SELECT FileContent FROM table WHERE ID = 1" queryout "C:\file.JPG" -T -N
原因是 bcp 将 Prefix Length 放在文件的最开头。它是 4 字节还是 8 字节,取决于 FileContent 列的数据类型(text、ntext、image:4 varchar(max)、varbinary(max):8 请参阅https://msdn.microsoft.com/en-us /图书馆/ms190779.aspx)
使用二进制编辑器(如 Visual Studio 中的编辑器)删除前缀字节,一切都运行良好。:-)
如果您有 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);
}
使用 Powershell
function SQLExecuteScalar([string]$pServer, [string]$pDatabase, [string]$pQuery)
{
# Connection
$pSQLConnection = New-Object System.Data.SqlClient.SqlConnection
$pSQLConnection.ConnectionString = "Data Source=$($pServer);Initial Catalog=$($pDatabase);Integrated Security=SSPI;Application Name=FileExtractor.Powershell"
$pSQLConnection.Open()
# Command
[System.Data.SqlClient.SqlCommand]$cmd = New-Object System.Data.SqlClient.SqlCommand($pQuery, $pSQLConnection)
# Execute and Get scalar value
[byte[]]$return = $cmd.ExecuteScalar()
# Close Connection
$pSQLConnection.Close()
# Result to pipe
return $return
}
[string]$Server = "MyServer"
[string]$DataBase = "MyDb"
[string]$Query = "select BlobValue from dbo.MyTable"
[string]$FileName = "C:\Temp\BlobValue.bin"
SQLExecuteScalar -pServer $Server -pDatabase $DataBase -pQuery $Query | Set-Content $FileName -Encoding Byte
只是一种选择。您可以使用免费软件 Toad for SQL server 并直接从编辑器中保存。
您可以访问他们的网站https://www.toadworld.com并在那里获取免费软件或完整版的 30 天试用版。它在下载并选择 Toad for SQL server。
您在 Toad 中对包含您要保存的图像的行执行常规选择语句。当您看到结果时,您可以单击字节图像列,如果这是 PDF 文档,您可以在右侧看到 PDF 选项卡,或者在左侧看到图像选项卡。单击选项卡时,您可以在底部看到保存徽标以保存图像或文件。
SQL 旨在处理数据库对象,因此从它的角度来看,其他任何东西都不存在。当然,有类似的扩展过程xp_cmdshell
允许您与操作系统交互,但它们是专有扩展,而不是 T-SQL 的一部分。
也许最接近的方法是对 SQL Server 2008 的二进制类型使用 FILESTREAM 属性,它允许将某些列直接作为文件存储在文件夹中,而不是使用数据库:
请注意,FILESTREAM 存储旨在维护数据库外的大文件以提高性能,而不是允许直接访问文件(即 T-SQL 仍然没有文件系统的概念)。我认为,从 SQL 直接访问文件系统将破坏数据库的某些目的(主要是以结构化方式存储数据)。
因此,我建议遵循 Dustin 的建议,使用 BCP 或任何其他数据转储器之类的工具。