我正在尝试计算学校项目数据库中的所有非空字段。请注意我不允许使用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 !');
每次运行代码时都会显示空的Db字段为数据类型为null的错误。
如何验证数据库字段为空?
答案 0 :(得分:2)
这是您的代码的新版本。我完成它的方式有点乱,但我认为如果我在其中包含注释和解释的话,最容易理解。
首先要说的是,虽然检查给定行的数据集字段为Null并不是坏事,但如果不允许数据库在可能被查询的列中存储Null,通常会更好。应用程序或原始Sql查询。从理论和实践的角度来看,全书的章节都是关于Nulls做什么或不做什么的。在实践中,他们通常被认为是“信息缺失”的意思。所以我们无法回答“这个用户想要一只狗吗?”的问题。在Null的存在。所以这是一个政策和设计选择的问题,如何处理Null,但通常在数据库的实现中通过对列施加NOT NULL约束来解决问题要好得多。
当我在评论中说Delphi不是特别擅长尊重Null和空白字段之间的区别时,我的意思是:在String字段的情况下,对于字段为Null的行,Delphi当您调用Field.AsString时,返回一个空字符串''。一些“纯粹主义者”会说,如果在包含Null的情况下要求其AsXXX属性,TField应该生成异常,因为它实际上不应该尝试“伪造”某个值。它的作用,即返回一个空字符串,0表示数字字段等,这是一个实用的妥协:它避免了初学者被Nulls的存在绊倒,但如果你想让你的代码处理Null,你可以使用TField.IsNull。
如果您遇到一个包含Null的数据库 - 并且您经常会感到压抑(除非您参与了数据库的设计) - 考虑一下它们最好在SQL中处理获取数据的可能性在您的Delphi代码看到它之前。
第二件事是,在没有明确的设计简报的情况下,我们并不真正知道“想要狗”的含义。这是否意味着用户想要一个单数,复数或是/否的狗? Yes / No是最简单的处理方式,但并非所有数据库都支持显式的布尔列类型,因此您经常会看到一个(希望是单字符)CHAR,VARCHAR(或它们的Unicode等价物)列。或整数列,如果要招待挑剔的用户的要求。
第三个问题是字段的Delphi DataType不一定与db的列类型完全相同,尽管它们之间存在标准映射,无论如何,通常最好使用值表示在Delphi代码中工作(例如.AsString,.AsInteger,.AsFloat,仅举几例)与db列类型最匹配。
不可否认,并非所有的数据库实现都很繁琐,对不起,我的意思是小心,因为Delphi正在处理列的数据类型,但大多数都是。一个值得注意的例外是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;
答案 1 :(得分:0)
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显示时它将按正确顺序...