這是您的代碼的新版本。這樣做有點混亂,但我認爲如果我在評論中出現問題的地方添加了評論和解釋,這將是最容易理解的。
首先要說的是,儘管檢查給定行的數據集字段爲Null並不是壞事,但如果數據庫不允許將Null存儲在可能被查詢的列中,通常會更好,通過應用程序或原始Sql查詢。整本書的章節都是從理論和實踐的角度寫出了空值的意義或不意味着什麼。在實踐中,他們通常被認爲僅僅意味着「缺少信息」。所以我們不能回答這個問題:「這個用戶想要一隻狗嗎?」存在一個空值。因此,關於Null的處理策略和設計選擇是一個問題,但是通過對列實施NOT NULL約束,在實現數據庫的過程中決定事情通常要好得多。
當我在評論中說德爾福在尊重空白和空白字段之間的差異方面不是特別好時,我的意思是這樣的:在字符串字段的情況下,對於字段爲空的行,當你調用Field.AsString時,Delphi返回一個空字符串''。有些「純粹主義者」會說,如果TField在包含Null時被要求提供AsXXX屬性,它應該會生成一個異常,因爲它不應該試圖「僞造」一個實際上沒有值的值。它的作用相反,即返回一個空字符串,0代表數字字段等等,這是一種務實的妥協方式:它避免了初學者因存在空值而被絆倒,但是如果你想讓你的代碼處理空值,你可以使用TField.IsNull。
如果你堅持使用包含空值的數據庫 - 並且經常令人沮喪的是(除非你在數據庫設計中有過手) - 考慮在SQL中最好處理它們的可能性,數據在你的Delphi代碼看到它之前。
第二件事是,如果沒有明確的設計說明,我們並不真正知道「想要一隻狗」究竟意味着什麼。例如,是否意味着用戶想要擁有單數,複數或是/否的狗? 「是/否」最簡單,但並非所有數據庫都支持顯式布爾列類型,所以您經常會看到(希望爲單字符)CHAR,VARCHAR(或其等同Unicode)列。或整數列,如果挑剔的用戶的需求。
第三件事是一個字段的Delphi數據類型不一定與數據庫的列類型完全相同,儘管它們之間有標準映射。無論如何,通常最好在你的Delphi代碼中使用值表示(例如.AsString,.AsInteger,.AsFloat,僅舉幾例),它們與db列類型最匹配。
不可否認,並非所有的數據庫實現都很挑剔,對不起,我的意思是小心,因爲德爾福正在處理列的數據類型,但大多數都是。一個值得注意的例外是Sqlite,儘管你可以在表的DDL中定義你的列類型,但Sqlite引擎將這些更像推薦,而不是規則,你可以在其中存儲幾乎任何東西。
正如我在前面的評論中所說的,Null是一個列/字段狀態,而不是一個值。所以問「什麼樣的數據類型是一個空的數據庫字段?」是各種「類別錯誤」。列的數據類型「正是它所說的」,也就是它定義在表的DDL中的數據類型。當然,你真正要問的是「如何確定該字段是否爲空」,這與Delphi編碼的設計選擇和數據庫實現一樣重要。
無論如何,足夠泛泛而談......
procedure CountDogsWanted;
var
// i,k : Integer ; <- names like i and k are usually as used for loop variables
DogsWanted : Integer;
Wanted : Boolean;
S : String; // contrast this naming style with what I said about the likes of i, j, k
// I've done this because we might want to do several tests & operations on its value
// and they will be easier to read with a shorter variable name. Not such a good idea
// when there are several such variables.
AField : TField;
const
scDogsWanted = 'Dogs wanted'; // this is to avoid making mistakes with typos
begin
{i := 0 ;}
DogsWanted := 0;
// The point of the following line is to retrieve the field we're working with
// only once, rather than doing a FieldByName (which involves a serial iteration
// through the dataset's Fields collection) for each row in the dataset.
// The AField variable will remain valid for the duration of this procedure
// or until the dataset is closed if its Fields aren't defined as persistent ones.
// Persistent fields remain valid for the lifetime of their owners (usually a
// datamodule or form). OTOH, "dynamic" fields are owned by the dataset and created
// and destroyed when the dataset is opened and closed.
AField := dmregisteredusers.tblusers.FieldByName(scDogsWanted);
{with dmregisteredusers do <- Don't use "with", it only ever causes problems}
{begin}
{tblusers.Sort := 'Nommer ASC'; <- pointless, it makes no difference when you count what order the things are in}
{tblusers.Edit; <- No! This puts the table into Edit state, but there's not point because you're not changing anything, and in any case, it will drop out of Edit state when you do a .Next}
dmregisteredusers.tblusers.First;
while not dmregisteredusers.tblusers.Eof do
{For k:= 1 to tblusers.RecordCount do <- Never, ever, iterate a dataset with a For loop.
The RecordCount is best avoided, too, because not all types of dataset return a meaningful
RecordCount }
begin
// You need to decide what to do about users whose 'Dogs wanted' field is Null
// If is is Null maybe we should ask for the record to be changed to indicate
// explicitly whether a dog is wanted or not
if AField.IsNull then begin
// to be filled in by you
end;
// You haven't told us what DataType the 'Dogs wanted' field is
// There are several possibilities. For simplicity, let's assume that the DataType of the field is ftString
// Let's also make a design decision that *only* if the field only contains a 'Y' in upper or lower
// case, then that means a dog is wanted.
// First copy the field's string value into a local variable so we don't have to keep getting it for the
// following operation;
S := dmregisteredusers.tblUsers.FieldByName(scDogsWanted).AsString;
S := Trim(S); { Trim() is a statndard function which removes leading and trailing whitespace }
Wanted := CompareText(S, 'Y') = 0; { CompareText does a case-insensitive comparison and returns zero if equal}
If
{ (tblusers['Dogs wanted'] = '') OR (tblusers['Dogs wanted'] = ' ')
OR (tblusers['Dogswanted'] = 0)}
Wanted
then
{tblusers.Next <- this is in the wrong place, you want to do a .Next regardless of the outcome of the "if"}
else
begin
inc(DogsWanted);
{tblusers.Next;}
end;//else
dmregisteredusers.tblusers.Next;
{end;//with}
end;//for
ShowMessage('There are ' + IntToStr(DogsWanted) + ' new dogs added to wishlist ,please contact the users regarding this matter and them remove the dogs from their wishlist !')
end;
['IsNull'](http://docwiki.embarcadero.com/Libraries/XE6/en /Data.DB.TField.IsNull) – TLama 2014-08-30 18:04:42
你似乎錯過了第三個'狗'後的空間。錯字?無論如何,通過使用結構「tblusers ['Dogs wanted']」,您可以訪問該領域的內容(如果有的話)作爲變體(請參閱OLH),並且我懷疑您目前有點太深入瞭解瞭解什麼麻煩,可以導致你與你目前的任務。使用tblusers.FieldByName('想要的狗')來到現場。它的DataType屬性會告訴你它是什麼類型。順便說一句,從來沒有你的「For」循環迭代數據集。使用「雖然不tblusers.Eof做......」之一。而且,你使用的數據庫類型是什麼? – MartynA 2014-08-30 19:24:39
此外,Delphi將數據集中任何給定列的數據類型視爲表中的每一行。 Null是* not *值,它是* state *,意味着該列(字段)對於所討論的行沒有值。設置你的項目的人應該已經解釋了這一點,並且空的<> NULL的事實。這是一個重要的區別。如果有一箇中間名稱的列,而您沒有,那麼該字段應該是空白的('')還是NULL?德爾福其實並不擅長尊重這種差異,但你需要清楚這一點。 – MartynA 2014-08-30 19:33:46