2014-08-30 103 views
1

我正在嘗試爲一個學校項目統計數據庫中的所有非空字段。請注意我不允許使用SQL。什麼數據類型是一個空的數據庫字段?

這裏是我每次運行的代碼是時間編碼

var i,k : Integer ; 
begin 
    i := 0 ; 
    with dmregisteredusers do 
    begin 
    tblusers.Sort := 'Nommer ASC'; 
    tblusers.Edit; 
    tblusers.First; 
    For k:= 1 to tblusers.RecordCount do 
    begin 
     If (tblusers['Dogs wanted'] = '') OR (tblusers['Dogs wanted'] = ' ') 
     OR (tblusers['Dogswanted'] = 0) 
     then 
     tblusers.Next 
     else 
     begin 
     inc(i); 
     tblusers.Next; 
     end;//else 
    end;//with 
    end;//for 
    ShowMessage('There are ' + IntToStr(i) + ' new dogs added to wishlist ,please contact the users regarding this matter and them remove the dogs from their wishlist !'); 

顯示了錯誤的數據庫字段是空的數據類型爲空的。

如何驗證數據庫字段是否爲空?

+0

['IsNull'](http://docwiki.embarcadero.com/Libraries/XE6/en /Data.DB.TField.IsNull) – TLama 2014-08-30 18:04:42

+0

你似乎錯過了第三個'狗'後的空間。錯字?無論如何,通過使用結構「tblusers ['Dogs wanted']」,您可以訪問該領域的內容(如果有的話)作爲變體(請參閱OLH),並且我懷疑您目前有點太深入瞭解瞭解什麼麻煩,可以導致你與你目前的任務。使用tblusers.FieldByName('想要的狗')來到現場。它的DataType屬性會告訴你它是什麼類型。順便說一句,從來沒有你的「For」循環迭代數據集。使用「雖然不tblusers.Eof做......」之一。而且,你使用的數據庫類型是什麼? – MartynA 2014-08-30 19:24:39

+0

此外,Delphi將數據集中任何給定列的數據類型視爲表中的每一行。 Null是* not *值,它是* state *,意味着該列(字段)對於所討論的行沒有值。設置你的項目的人應該已經解釋了這一點,並且空的<> NULL的事實。這是一個重要的區別。如果有一箇中間名稱的列,而您沒有,那麼該字段應該是空白的('')還是NULL?德爾福其實並不擅長尊重這種差異,但你需要清楚這一點。 – MartynA 2014-08-30 19:33:46

回答

2

這是您的代碼的新版本。這樣做有點混亂,但我認爲如果我在評論中出現問題的地方添加了評論和解釋,這將是最容易理解的。

首先要說的是,儘管檢查給定行的數據集字段爲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; 
+0

雖然使用常量比重複自己更好,但是對該字段的引用的變量會更好。 – TLama 2014-08-30 20:43:44

+1

@TLama:我完全同意,但只是想知道這樣做是否會掩蓋這樣一個觀點,即堅持引用該字段的名稱是一個很好且必要的第一步,尤其是對於帶有嵌入空白字段的字段名(我個人不贊成)。我會在我的下一個傳球中完成。 – MartynA 2014-08-30 20:59:42

0

傢伙,就像我欣賞你把答案我不能把它們作爲我的教練就知道它不是我自己的工作努力... @MartynA你的編碼有我的問題的答案 這裏是新的編碼 我知道它的馬虎,還有很多其他的方式,它可以工作,但我真的時間緊迫,所以不能花時間學習代碼...這是什麼我會用

For k:= 1 to tblusers.RecordCount do 
begin 
If dmregisteredusers.tblusers.FieldByName('Dogs wanted').IsNull then tblusers.Next 
else 
    begin 
    inc(i) ; 
    tblusers.Next; 
    end//then begin 

我使用了db form.onshow的排序,所以當DBGrid顯示它時,它的排列順序是正確的...

+0

當然,我們都知道什麼是在時間壓力下工作,沒有任何問題,反正這樣對未來的讀者來說,就像提問者的直接需求一樣。但是如果我是你的話,我會在指導員的前額上留下一個筆記來解釋空值及其導致的問題,並且說在你的代碼中,你有效地假設Null的意思是「沒有狗,謝謝」,這可能不反映數據是如何輸入的。 – MartynA 2014-08-30 21:05:28

+0

是的..用戶要求一隻狗,管理員必須被通知有多少人想要一隻狗,如果不是零,這意味着他們想要一隻狗...聽起來很容易,直到你必須將它轉換成代碼...還使用代碼輸入數據到數據庫中,因此只有某些值可以出現在字段中。 – 2014-08-30 21:08:39

相關問題