2014-08-30 99 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