为不同目的存储图像的最佳方法(关于数据库设计)是什么?
我有一堆用户照片,我还有另外5套不同的照片(例如用户照片,但没有连接到用户照片)。
最好将所有照片存储在一个数据库表中并尝试从该表中引用它们,还是最好为每组照片创建不同的表?
我可以看到创建多个表的一个好处,那就是删除主对象时删除照片的级联删除功能。
还需要考虑其他方面吗?
另一个例子可能是地址。用户可以拥有地址,但公司或位置也可以。 为所有地址创建一个表,并尝试使用某种索引表来引用哪个地址属于哪个对象或具有不同的表并消除问题。
答案 0 :(得分:14)
在SQL Server中存储大量二进制数据并不是一个好方法。它使您的数据库非常庞大,备份和性能通常不是很好。存储文件通常在文件系统上完成。 Sql Server 2008具有对FILESTREAM
的开箱即用支持。
Microsoft将案例记录为使用 FileStream ,如下所示
在你的情况下,我认为所有要点都是有效的。
要在服务器上启用FILESTREAM
支持,请使用以下语句。
EXEC sp_configure filestream_access_level, 2
RECONFIGURE
要获取链接到数据库的文件流文件组,请创建
ALTER DATABASE ImageDB ADD FILEGROUP ImageGroup CONTAINS FILESTREAM
ALTER DATABASE ImageDB
ADD FILE ( NAME = 'ImageStream', FILENAME = 'C:\Data\Images\ImageStream.ndf')
TO FILEGROUP TodaysPhotoShoot
下一步是使用文件流存储将数据存储到数据库中:
CREATE TABLE Images
(
[Id] [uniqueidentifier] ROWGUIDCOL NOT NULL PRIMARY KEY,
[CreationDate] DATETIME NOT NULL,
[ImageFile] VARBINARY(MAX) FILESTREAM NULL
)
要让Filestream
工作,您不仅需要表中字段的FILESTREAM
属性,还需要具有ROWGUIDCOL
属性的字段。
现在要在此表中插入数据,您可以使用TSQL:
using(var conn = new SqlConnection(connString))
using(var cmd = new SqlCommand("INSERT INTO Images VALUES (@id, @date, cast(@image as varbinary(max))", conn))
{
cmd.Parameters.AddRange(new {
new SqlParameter("id", SqlDbType.UniqueIdentifier).Value = uId,
new SqlParameter("date", SqlDbType.DateTime).Value = creationDate,
new SqlParameter("image", SqlDbType.varbinary).Value = imageFile,
});
conn.Open
cmd.ExecuteScalar();
}
SqlFileStream
还有一种方法可以直接使用Win32在磁盘上获取文件数据。这为您提供了从SqlFileStream
继承的流媒体访问IO.Stream
。
使用win32插入数据可以通过以下代码完成:
public void InsertImage(string connString, Guid uId, DateTime creationDate, byte[] fileContent)
{
using (var conn = new SqlConnection(connString))
using (var cmd = new SqlCommand(@"INSERT INTO Images VALUES (@id, @date, cast(@image as varbinary(max)) output INSERTED.Image.PathName()" , conn))
{
conn.Open();
using (var transaction = conn.BeginTransaction())
{
cmd.Transaction = transaction;
cmd.Parameters.AddRange(
new[] {
new SqlParameter("id", SqlDbType.UniqueIdentifier).Value = uId,
new SqlParameter("date", SqlDbType.DateTime).Value = creationDate,
new SqlParameter("image", SqlDbType.VarBinary).Value = null
}
);
var path = (string)cmd.ExecuteScalar();
cmd.CommandText = "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()";
var context = (byte[])cmd.ExecuteScalar();
using (var stream = new SqlFileStream(path, context, FileAccess.ReadWrite))
{
stream.Write(fileContent, 0, fileContent.Length);
}
transaction.Commit();
}
}
使用文件流方法存储图像时,表格非常窄,这有利于提高性能,因为每8K数据页面可以存储许多记录。我会使用以下模型:
CREATE TABLE Images
(
Id uniqueidentifier ROWGUIDCOL NOT NULL PRIMARY KEY,
ImageSet INTEGER NOT NULL
REFERENCES ImageSets,
ImageFile VARBINARY(MAX) FILESTREAM NULL
)
CREATE TABLE ImageSets
(
ImageSet INTEGER NOT NULL PRIMARY KEY,
SetName nvarchar(500) NOT NULL,
Author INTEGER NOT NULL
REFERENCES Users(USerId)
)
CREATE TABLE Users
(
UserId integer not null primary key,
UserName nvarchar(500),
AddressId integer not null
REFERENCES Addresses
)
CREATE TABLE Organsations
(
OrganisationId integer not null primary key
OrganisationName nvarchar(500),
AddressId integer not null
REFERENCES Addresses
)
CREATE TABLE Addresses
(
AddressId integer not null primary key,
Type nvarchar(10),
Street nvarchar(500),
ZipCode nvarchar(50),
City nvarchar(500),
)
CREATE TABLE OrganisationMembers
(
OrganisationId integer not null
REFERENCES Organisations,
UserId integer not null
REFERENCES Users,
PRIMARY KEY (UserId, OrganisationId)
)
CREATE NONCLUSTERED INDEX ixOrganisationMembers on OrganisationMembers(OrganisationId)
这转换为以下实体关系图:
参考文献:
答案 1 :(得分:0)
拥有不同表格的唯一原因是您可以拥有FK。但这对于数据完整性非常重要。
如果您想对所有照片进行单一查询,那么拥有所有照片的单个表的一个原因就是。
另一个原因可能是它使您的应用程序编写更容易(即因为您不必更改在单个照片表中工作的代码)
由于第二和第三个原因很不可能,我建议您使用第一个选项。
答案 2 :(得分:0)
当我有某种实体在多个上下文中重复时,例如邮寄地址,我经常将它们全部收集在一张桌子上。这通常简化了验证(例如邮政编码),管理重复,......
在适当的情况下,我会有一个交叉参考表。例如,电话号码可能与一个音符(“家”,“移动”,......)一起存在于一个表中。供应商和电话号码之间的交叉参考表可以将一个人与所需的电话号码相匹配。它还提供了添加排名的机会,以便他们可以指定他们的首选电话号码。在某些情况下,您可能希望提示用户更新有关相关更改的信息,例如当您更新公司的800号码时,是否应该更新其他任何引用?
无论如何,删除都需要检查对实体的任何未完成引用。在大多数应用程序中,这种情况不会频繁发生,从而导致出现问我不是使用级联删除的忠实粉丝。我宁愿有一个存储过程来管理删除并处理任何“手动”级联,以避免真正的重大意外。
BLOB是另一个讨论。照片,PDF文档和其他庞大的二进制文件在数据库大小,命名约定,备份/恢复等方面存在问题。根据所使用的SQL Server的特定版本,这些会有所不同。
答案 3 :(得分:0)
从包含任何类型的大数据的表中检索行需要时间。图像往往非常大,如果我要设计一个数据库,在其结构中存储图像或其他大文件,那么我会:
答案 4 :(得分:0)
FileStream没问题,如上所述。但它很复杂。你知道什么是最好的存储文件?文件系统。这就是它的作用。您只需要设置一个所有Web服务器都可以写入的共享,并且您的保存过程是1)生成图像ID,2)使用该名称保存文件,3)插入指定文件共享网络的行路径或文件的URL。然后,您的db表保持小而快,客户端可以从文件系统中提取文件。在SSD上设置带有RAID的TB级文件服务器来存储文件并将访问路径存储在数据库服务器中更便宜,更快速,更可靠。 BLOB在sql server中有奇怪的效果,比如一旦删除就不会放弃它们的空间,还有很多其他问题(不能在线重建聚簇索引等)。