線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:2218
推到 Plurk!
推到 Facebook!

如何高效存取SQL Server 2000中BLOB字段

答題得分者是:jieshu
jmjunta52372937
一般會員


發表:1
回覆:2
積分:0
註冊:2005-04-27

發送簡訊給我
#1 引用回覆 回覆 發表時間:2005-04-30 23:00:19 IP:219.138.xxx.xxx 未訂閱
在下在开发中碰到一个问题,就是如何高效存取SQL Server 2000中BLOB字段。请各位高手赐教。

问题背景:在开发基于SQL Server 2000的一个传统c/S数据库应用程序时,一个用户表中有多个image(即BLOB)类型的字段(现以三个字段bFacePhoto、bSidePhoto、bAllPhoto来说明问题),用于存放三幅图(JPG格式的,已经过减肥,每幅图大小不等,最小的45KB,最大的100多KB),存取现在都是没有问题的,问题的核心是在取出相应的记录时,有相当的延时。

我用的是开发工具是delphi 7,用ADO访问数据库,现在我想加入ClientDataSet和DataSetProvider,并设置DataSetProvider 的poFetchBlobsOnDemand为True,想利用这个缓冲数据集在解决读取图像时严重延时的问题,我想在列记录时先不取出BLOB信息,等用户想查看图像时,就再读取相应记录的BLob信息,我调用了Fill_Picture(DataSet)过程完成图像读取,代码如下:

[code delphi]
procedure TFrmNew.Fill_Picture(DataSet: TDataSet);
var
sFileName1, sFileName2, sFileName3: string;
tempstream1, tempstream2, tempstream3: TStringStream;
tempjpeg1, tempjpeg2, tempjpeg3: TJPEGImage;
begin
if not DataSet.IsEmpty then
begin
try
tempstream1 := TStringStream.Create(' ');
tempstream2 := TStringStream.Create(' ');
tempstream3 := TStringStream.Create(' ');
if (not Dataset.FieldByName('bFacePhoto').IsNull) then
begin
TBlobField(Dataset.FieldByName('bFacePhoto')).SaveToStream(tempstream1);
tempstream1.Position := 0;
if not Assigned(tempjpeg1) then
tempjpeg1 := TJPEGImage.Create;
tempjpeg1.LoadFromStream(tempstream1);
Img1.Picture.Bitmap.Assign(tempjpeg1);
sFileName1 := ExtractFilePath(Application.ExeName) 'tmpFaceBlob';
sFileName1 := sFileName1 '.Jpg';
img1.Picture.SaveToFile(sFileName1);
end
else
begin
Img1.Picture.Graphic := nil; //清空正面照图像
end;
if (not Dataset.FieldByName('bSidePhoto').IsNull) then
begin
TBlobField(Dataset.FieldByName('bSidePhoto')).SaveToStream(tempstream2);
tempstream2.Position := 0;
if not Assigned(tempjpeg2) then
tempjpeg2 := TJPEGImage.Create;
tempjpeg2.LoadFromStream(tempstream2);
Img2.Picture.Bitmap.Assign(tempjpeg2);
sFileName2 := ExtractFilePath(Application.ExeName) 'tmpSideBlob';
sFileName2 := sFileName2 '.Jpg';
img2.Picture.SaveToFile(sFileName2);
end
else
begin
Img2.Picture.Graphic := nil; //清空侧面照图像
end;
if (not Dataset.FieldByName('bAllPhoto').IsNull) then
begin
TBlobField(Dataset.FieldByName('bAllPhoto')).SaveToStream(tempstream3);
tempstream3.Position := 0;
if not Assigned(tempjpeg3) then
tempjpeg3 := TJPEGImage.Create;
tempjpeg3.LoadFromStream(tempstream3);
Img3.Picture.Bitmap.Assign(tempjpeg3);
sFileName3 := ExtractFilePath(Application.ExeName) 'tmpAllBlob';
sFileName3 := sFileName3 '.Jpg';
img3.Picture.SaveToFile(sFileName3);
end
else
begin
Img3.Picture.Graphic := nil; //清空全身照图像
end;
OleContainer1.Visible := False;
finally
if sFileName1 <> '' then deleteFile(sfileName1);
if sFileName2 <> '' then deleteFile(sfileName2);
if sFileName3 <> '' then deleteFile(sfileName3);
if Assigned(tempjpeg1) then
tempjpeg1.Free;
if Assigned(tempjpeg2) then
tempjpeg2.Free;
if Assigned(tempjpeg3) then
tempjpeg3.Free;
tempstream1.Free;
tempstream2.Free;
tempstream3.Free;
end;
end;
end;
[/code]

因此我在ClientDataSet的AfterScroll中:
[code delphi]
procedure TFrmNew.ClientDataSet1AfterScroll(DataSet: TDataSet);
begin
Fill_Picture(DataSet);
end;
[/code]
但问题出来了,设置DataSetProvider 的poFetchBlobsOnDemand为True时出现了错误"Blob has not been fetched",即BLOB字段在此时是没有被取回来的,但我想在用户在DBGridEh1中移动光标定位到下一行时就取出图像,其实是不应该在ClientDataSet1AfterScroll中调用Fill_Picture 的,因为这样一旦ClientDataSet1.Open后就会执行Fill_Picture, 如果不在ClientDataSet1AfterScroll中调用Fill_Picture 请问我该如何做?谢谢了。

另ClientDataSet1.FetchBlobs如何用

精通它,才能毁灭它。无知者无畏。
------
精通它,才能毁灭它。无知者无畏。
編輯記錄
GrandRURU 重新編輯於 2016-05-15 19:07:31, 註解 無‧
jieshu
版主


發表:41
回覆:894
積分:745
註冊:2002-04-15

發送簡訊給我
#2 引用回覆 回覆 發表時間:2005-05-02 17:03:04 IP:203.204.xxx.xxx 未訂閱
引言:
procedure TFrmNew.ClientDataSet1AfterScroll(DataSet: TDataSet);
begin
  ClientDataSet1.FetchBlobs;
  Fill_Picture(DataSet);
end;
但问题出来了,设置DataSetProvider 的poFetchBlobsOnDemand为True时出现了错误"Blob has not been fetched",即BLOB字段在此时是没有被取回来的,但我想在用户在DBGridEh1中移动光标定位到下一行时就取出图像,其实是不应该在ClientDataSet1AfterScroll中调用Fill_Picture 的,因为这样一旦ClientDataSet1.Open后就会执行Fill_Picture, 如果不在ClientDataSet1AfterScroll中调用Fill_Picture 请问我该如何做?谢谢了。
如果要在User移動光標就取出圖像, 當然就一定要寫在AfterScroll事件, 且只抓一筆的圖檔應該還好, 我用一個簡單的範例(DBDEMOS的animals轉入MS SQL)測試了一下, 在取用前下FetchBlobs即可, 亦可在ClientDataSet上設FetchOnDemand為True就會自動抓取, 不用再下FetchBlobs, 不過一定要有一個Primary Key的Index, 這就是我為什麼要轉入MS SQL的原因. <iFrame src="http://www.coss.com.tw/jieshu/sign.htm" width=400 height=105 scolling="NO" border="0"></iFrame> 震江系統(股)公司: http://www.coss.com.tw/ 捷舒軟體設計坊: http://www.coss.com.tw/jieshu/
------
人生有夢,逐夢而行
人若為善,福雖未至,禍已遠離
人若為惡,禍雖未至,福已遠離
http://www.taconet.com.tw/jieshu/
jmjunta52372937
一般會員


發表:1
回覆:2
積分:0
註冊:2005-04-27

發送簡訊給我
#3 引用回覆 回覆 發表時間:2005-05-04 10:21:55 IP:219.138.xxx.xxx 未訂閱
首先无论如何要谢谢jieshu,但我按所述 procedure TFrmNew.ClientDataSet1AfterScroll(DataSet: TDataSet); begin ClientDataSet1.FetchBlobs; Fill_Picture(DataSet); end; 执行出错,经调试跟踪,发现是在 ClientDataSet1.FetchBlobs一句出现错误: "Record not found or Changed by another User".我确信表只有我一个用户打开。这是在设置DataSetProvider 的poFetchBlobsOnDemand为True情形下试的.原因为何,不得而解,请赐教,谢谢! 精通它,才能毁灭它。无知者无畏。
------
精通它,才能毁灭它。无知者无畏。
JustinShen
中階會員


發表:22
回覆:104
積分:80
註冊:2003-09-20

發送簡訊給我
#4 引用回覆 回覆 發表時間:2005-05-05 15:22:39 IP:221.225.xxx.xxx 未訂閱
引言: 执行出错,经调试跟踪,发现是在 ClientDataSet1.FetchBlobs一句出现错误: "Record not found or Changed by another User".我确信表只有我一个用户打开。这是在设置DataSetProvider 的poFetchBlobsOnDemand为True情形下试的.原因为何,不得而解,请赐教,谢谢!
正如Jieshu所言,必须有一个PrimaryKey。但是只有PrimaryKey而没有设置每个字段的ProviderFlags或UpdateMode仍然没有用,执行FetchBlobs时ClientDataSet还是传回了所有字段值来获取Blobs字段,因此失效了。 Justin Shen
------
====================
我为一切作努力!
Justin Shen

pcplayer99
尊榮會員


發表:142
回覆:738
積分:591
註冊:2003-01-21

發送簡訊給我
#5 引用回覆 回覆 發表時間:2005-05-05 19:51:08 IP:219.133.xxx.xxx 未訂閱
你的做法本身是错误的。 不应该一次取N条包含了BLOB的Record。 因为BLOB比较大,如果都取出来,DataBase Server会比较累,将Data传输到Client端也会比较耗时。 通常,应该是只select出不包含Blob Field的record,放进DBGrid里让User浏览。当User选中某条Record的时候,再根据改条record的Key,向DataBase Server请求该条Record的Blob Data,拿到Blob Data后,再Show它。
系統時間:2017-10-23 8:59:21
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!