我从db获取值,当值 NULL 时出现错误
无法将类型(null)的变体转换为类型(整数)
如果我使用 0 填充数据库而不是任何内容( NULL ),则错误消失
所以说我有这个
OneSpell.PerCent := FQuery.Recordset.Fields[ DB_FLD_PER_CENT ].Value;
OneSpell.Plus := TCardPlus ( FQuery.Recordset.Fields[ DB_FLD_PLUS ].Value );
OneSpell.Quantity := FQuery.Recordset.Fields[ DB_FLD_QUANTITY ].Value;
有没有办法说明值是 NULL 然后使其 0 ?或者这必须通过数据库完成。
FQuery
是adoquery
,db是访问
我知道我能做到
if .... = null then
onespell.plus := 0
else
.........
但我想在onepell
的每个值的一行中做到这一点答案 0 :(得分:8)
这是常见的方法:
提取重复部分以保持代码DRY并提高可读性
function IfNull( const Value, Default : OleVariant ) : OleVariant;
begin
if Value = NULL then
Result := Default
else
Result := Value;
end;
...
OneSpell.Plus := IfNull( FQuery.Recordset.Fields[ DB_FLD_PER_CENT].Value, 0 );
答案 1 :(得分:6)
因为您正在使用ADOQuery.Recordset
,它返回对底层_Recordset
的引用(不是来自Delphi,而是直接是ADO对象引用),您的选择仅限于那些ADO直接支持。 Delphi的TADOQuery
具有包装方法,可以隐藏在较低级别处理ADO的大量复杂性,而使用Recordset
会严重限制您的选项。
AFAICT,在一行中执行此操作的唯一方法是使用长行,它同时使用VarIsNull
函数和Math.IfThen
函数(默认为{如果省略0
参数,则{1}}:
AFalse
您可以使用中间OneSpell.PerCent := IfThen(not VarIsNull(FQuery.Recordset.Fields[ DB_FLD_PER_CENT].Value), FQuery.Recordset.Fields[DB_FLD_PER_CENT].Value);
变量(仍然需要OleVariant
单位)更可读地使用两行:
Math
最简单的替代方法(无论如何,从Delphi代码的角度来看)将在SQL语句本身中使用var
Val: OleVariant;
...
Val := FQuery.Recordset.Fields[DB_FLD_PER_CENT].Value;
OneSpell.PerCent := IfThen(not VarIsNull(Val), Val);
或IsNull
或其在MS Access中的等价物处理此问题,因此您不需要不得不在你的应用程序代码中担心它;你可以简单地访问Coalesce
并知道它包含一个整数。
实际上,最简单替代方法是,如果您实际上不需要这样做,则根本不使用Value
(因为RecordSet
结果中有多个记录集)。如果您只是直接使用Delphi的TADOCommand
,您只需使用原生TADOQuery
属性,它将自动为您处理转换:
TField.AsXXX
答案 2 :(得分:4)
停止在delphi代码中工作,你的查询应该处理这个问题。大多数数据库后端支持COALESCE功能。因此,在您的查询中,只需使用:SELECT COALESCE(MyIntegerField, 0) FROM Foo
。如果MyIntegerField
包含NULL
值,COALESCE
将返回第一个non-NULL
值,在这种情况下为0
修改强>
刚刚意识到您的数据库后端是MS Access,在这种情况下使用IIF
函数:
SELECT Percent= IIf([Percent] Is Null, 0, [Percent]) FROM Foo
答案 3 :(得分:2)
如果您知道您总是希望将Null解释为false布尔值,0数值或空字符串,则可以设置System.Variants.NullStrictConvert = false。
(严格地说,对于Null到字符串转换,将采用System.Variants.NullAsStringValue的值。)
答案 4 :(得分:2)
会尝试展示另外两种变体,这些变体在开箱即用。
1:使用TDataSet原生访问
OneSpell.Plus := TCardPlus ( FQuery.FieldByName( 'DB_FLD_PLUS' ).AsInteger );
或
OneSpell.Plus := TCardPlus ( FQuery.Fields[ 2 ].AsInteger );
假设“DB_FLD_PLUS”是查询中的第三列。对于空(NULL)列,TField.AsInteger
返回零。
2:使用Delphi高级记录
type
TSpellPlus = record
Value : TCardPlus;
class operator Implicit( const from: TCardPlus ): TSpellPlus; inline;
class operator Implicit( const from: Variant ): TSpellPlus;
class operator Implicit( const from: TSpellPlus ): TCardPlus; inline;
end;
TOneSpell = record
private
function GetCardPlus: TCardPlus; inline;
procedure SetCardPlus( const Value: TCardPlus ); inline;
public
var PerCent : integer;
var PlusVar : TSpellPlus;
property Plus : TCardPlus read GetCardPlus write SetCardPlus;
var Quantity : cardinal;
end;
function TOneSpell.GetCardPlus: TCardPlus;
begin
Result := Self.PlusVar;
end;
procedure TOneSpell.SetCardPlus( const Value: TCardPlus );
begin
Self.PlusVar := Value;
end;
class operator TSpellPlus.Implicit( const from: TSpellPlus ): TCardPlus;
begin
Result := From.Value;
end;
class operator TSpellPlus.Implicit( const from: TCardPlus ): TSpellPlus;
begin
Result.Value := From;
end;
class operator TSpellPlus.Implicit( const from: Variant ): TSpellPlus;
var i: integer;
begin
if VarIsNull( From )
then i := 0
else i := From;
Result.Value := TCardPlus( i );
end;
....
OneSpell.PlusVar := FQuery.Recordset.Fields[ DB_FLD_PLUS ].Value;
然后这三行是等价的:
var cp: TCardPlus;
cp := OneSpell.Plus;
cp := OneSpell.PlusVar;
cp := OneSpell.PlusVar.Value;
答案 5 :(得分:0)
有趣的帖子和有趣的答案,无论如何,没有提到的内容,至少在所有答案中都很清楚,并且可能看起来非常明显,但对于 Delphi/Firedac 的新手来说,您需要包括的不是那么多(添加到模块的使用)接口 SYSTEM.VARIANTS 以询问 datafield.value = NULL 或 <> NULL。