Delphi中很多朋友使用ADOQuery更新數據時會出現,無法爲更新定位行。一些值可能已在最後一次讀取後已更改的錯誤信息。
以前的時候也碰到類似的問題,一般就是去看數據表,發現沒有設置主鍵或者表裏的字段有的沒有默認值什麼的。說來也怪,設置上主鍵或者默認值都基本搞 定。也就沒有仔細研究錯誤的原因。但是今天又碰到類似的問題,還是照以前的解決辦法去設置,結果無語了,怎麼搞都解決不了問題,在網上查找了相關資料得到 如下解決方法:
在TADOQuery打開後加入TADOQuery(DataSet).Properties.Get_Item(‘Update Criteria’).Value :=0 語句。
示列:
procedure TfrmMain.aqryPubAfterOpen(DataSet: TDataSet);
begin
TADOQuery(DataSet).Properties.Get_Item(‘Update Criteria’).Value :=0;
end;
就是將“TADOQuery(DataSet).Properties.Get_Item(‘Update Criteria’).Value := 0;”加到你的要保存的ADOQuery控件的“AfterOpen”事件裏面,應該是適用於所有的ADOQuery的。
TADOQuery(DataSet).Properties[]就爲設置ADO地選項.
Update Criteria爲自動生成SQL語句時WHERE語句地生成方式,取值如下:
adCriteriaKey = 0: 僅使用主鍵
adCriteriaAllCols = 1: 使用所有字段
adCriteriaUpdCols = 2: 僅使用修改地字段(默認值)
adCriteriaTimeStamp = 3: 使用時間戳字段(假如有地話)
原文地址:http://www.radxe.com/?p=527 | 易勝領地-Delphi編程
ADOQuery->Post更新問題: 無法爲更新定位行。一些值已在最後一次讀取後已更改...
原因:表中的數據和需要更新的數據一致,無須修改,當使用mysql時需要判斷(mssql不需要)
錯誤寫法:
ADOQuery1->Edit();
ADOQuery1Score->Value = 100;
ADOQuery1->Post();
ADOQuery1->Close();
正確寫法:
ADOQuery1->Edit();
if( ADOQuery1Score->Value!=100) //先判斷是否相等,再修改
ADOQuery1Score->Value = 100;
ADOQuery1->Post();
ADOQuery1->Close();
解法三:
在Delphi中,如果使用ADOQuery插入數據沒有問題, 之後對數據進行修改保存時,就會遇到“無法爲更新定位行,一些值可能已在最後一次讀取後已更改”的問題。 原因有這樣幾種: 1.在數據庫設計時,爲某些字段設置了默認值,在修改進行提交以後,數據庫會自動修改對應字段的所有行的默認值,從而導致了數據庫與數據集中數據的不一致,使ADOQuery無法對數據集進行定位。 2.數據庫對應的表沒有主鍵,輸入了重複的數據以後,數據庫裏有兩條一樣的數據,從而使ADOQuery無法對數據進行定位。 解決方法: 1.修改數據庫設計,不再設置默認值,爲數據庫表定義主鍵,保證其唯一性。 2.在執行完ADOQuery.Post之後,執行ADOQuery.Refresh,對於設置默認值的情況可以解決。 (refresh後dataset中的默認值字段獲得了值,跟數據庫中一致了) 3.改用Insert into sql語句插入,而不是add--post方式. 但這種方式不更新其他打開該表的query, 所以要requery纔行, refresh不起作用. -------------------------------------------------------------------------- 外一篇關於oracle中出現該問題的文章: 分析解決delphi的bug:“無法爲更新定位行。一些值可能已在最後一次讀取後已更改。” Delphi在使用ADO操作oracle數據庫時,經常會出現“無法爲更新定位行。一些值可能已在最後一次讀取後已更改。”的錯誤,一直沒有找到好的解決辦法,但同樣得代碼在SQLServer上卻沒有問題。上回看到了eygle 如何跟蹤oracle的文章有所啓發,對程序的數據庫操作進行了跟蹤,找到了問題的所在。 分析錯誤出現的情況 1. 在使用DBGrid對錶進行編輯時,添加一行數據,再修改新添加行中的原來爲空的一個字段(文本型),改變光標到其它行,此時提示錯誤。 分析:添加不報錯,修改提交時報錯,分析可能是執行UPDATE時報錯。 2. 對一條數據中文本字段進行第二次修改時提示同樣錯誤。 分析:第一次修改不報錯,第二次就報錯,兩次都是執行UPDATE,區別可能就是,第一次和第二次的一些狀態不同了。 跟蹤ORACLE,得到執行錯誤時的腳本 UPDATE "測試" SET "檔號"=:V00001 WHERE "檔號"=:V00002 AND "ROWID"=:V00003 Bind#0 value="eee" Bind#1 Bind#2 value="AAAD7CAAGAAACgPAAI" 跟蹤ORACLE,得到執行正確時的腳本 UPDATE "測試" SET "檔號"=:V00001 WHERE "檔號" IS NULL AND "ROWID"=:V00002 Bind#0 value="eee" Bind#1 value="AAAD7CAAGAAACgPAAI" 根據以上跟蹤的結果,找到了問題的所在,"檔號"=’’ 和 "檔號" IS NULL 在ORACLE是不同的, "檔號"=’’在ORACLE 不能定位到正確的記錄行,所以造成了更新的失敗。 我們知道ORACLE中文本型字段有兩種狀態 NULL 和NOT NULL,而MSSQL中有三種狀態 NULL,NOT NULL 和’’ ,這就是爲何以上程序在MSSQL中運行正常,而在ORACLE中卻不能正常的原因了。(注:Oracle中如設置一個文本字段=’’,Oracle會自動將其轉換爲NULL,但是在查詢時如果這樣用就不行,不知能不能算是ORACLE的一個BUG。) 爲何在第一次編輯時能正確生成代碼,而在第二次就不可以了。原因就是Delphi中的TField的屬性IsNULL,第一次其從數據庫中取出了正確的狀態爲IsNull=True,編輯之後,如果數值=’’,其IsNull就是False,而TAdoQuery生成更新腳本時就是根據這個屬性來生成=’’和 IS NULL這兩種腳本的。 找到問題,我們就可以寫代碼來解決問題了。分析以上問題,我們只要在使用ORACLE數據庫時,在提交前,將文本字段=’’字段的IsNull=True,就可以了,我們在TADOQuery的BeforePost事件中添加代碼,代碼如下: procedure TFrmData.adoqMainBeforePost(DataSet: TDataSet); var i: Integer; begin if DATA = 'ORACLE' then //自定義,標明數據庫類型 for i := 0 to DataSet.FieldCount - 1 do begin if DataSet.Fields[i].IsNull then exit; if DataSet.Fields[i].DataType in [ftString, ftFixedChar, ftGuid, ftWideString] then if DataSet.Fields[i].AsString = '' then DataSet.Fields[i].Clear; end; end;