我有一堆用户的照片,另有5套不同的照片(如用户照片,但没有连接到用户的照片).
将所有照片存储在单个数据库表中是最好的事情,并尝试从该表中引用它们,或者最适合为每组照片创建不同的表格?
我可以看到创建多个表的一个好处,这是删除主对象时删除照片的级联删除功能.
任何其他方面要考虑?
另一个例子可能是地址.用户可以拥有地址,但公司或位置也可以.
为所有地址创建一个表,并尝试使用某种索引表来引用哪个地址属于什么对象或具有不同的表,并消除问题.
解决方法
在sql Server中存储大量二进制数据不是一个好办法.它使您的数据库非常庞大的备份和性能一般不是很大.存储文件通常在文件系统上完成. sql Server 2008具有对FILESTREAM的即时支持.
Microsoft以如下方式记录使用FileStream的情况
>正在存储的对象平均大于1 MB.
>快速阅读访问很重要.
>您正在开发使用中间层应用程序逻辑的应用程序.
在你的情况下,我认为所有的积分都是有效的.
在服务器上启用
要在服务器上启用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
创建表
下一步是使用filestream存储将您的数据存储在数据库中:
CREATE TABLE Images ( [Id] [uniqueidentifier] ROWGUIDCOL NOT NULL PRIMARY KEY,[CreationDate] DATETIME NOT NULL,[ImageFile] VARBINARY(MAX) FILESTREAM NULL )
对于Filestream工作,您不仅需要表中字段上的FILESTREAM属性,还需要具有ROWGUIDCOL属性的字段.
用Tsql插入数据
现在要在这个表中插入数据,你可以使用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,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.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,fileContent.Length); } transaction.Commit(); } }
如何建模照片存储数据库
使用filestream方法存储图像,表格非常窄,这对于性能有好处,因为每个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)
这转换为以下实体关系图:
>性能方面,窄图表非常好,因为它每个记录只包含几个字节的数据.
>我们可以假设一个图像始终是图像集的成员,如果只有一个图像,则该信息可能被隐藏.
>我假设你想跟踪哪些用户是哪个组织的成员,所以我添加了一个表来链接它们(假设用户可以是多个组织的成员).
> OrganisationMembers表中的主键将UserId作为第一个字段,因为通常用户多于Organizations,而您可能会想要显示哪个组织的成员比倒数更频繁.
>组织机构中的OrganisationId指数成员是为了满足特定组织成员名单需要显示的查询.
参考文献:
> Enable and configure Filestream
> Create a filestream enabled Database
> Create a table for storing filestream data
> Managing Filestream with Transact SQL
> Create Client applications with Filestreams