【问题标题】:how to check if null value in single line如何检查单行是否为空值
【发布时间】:2014-01-11 00:49:34
【问题描述】:

我从 db 获取一个值,当该值为 NULL 时出现错误

无法将类型 (null) 的变体转换为类型 (Integer)

如果我用 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?还是必须通过数据库来完成。

FQueryadoquery 并且 db 是访问权限

我知道我能做到

if  .... = null then 
  onespell.plus := 0 
else
   .........

但我想为 onespell 的每个值在一行中执行此操作

【问题讨论】:

  • 什么是OneSpellPerCentPlusQuantity 是什么类型? FQueryTADOQuery 还是别的什么?你能说得更具体一些吗,以便我们尝试帮助你? (是的,如果你想要 0 而不是 NULL,减少代码的最简单的解决方案是在 SQL 本身中使用 IsNull()Coalesce() 或在你使用的任何未指定的数据库系统中的任何等效项,因为这样您的应用程序代码就不需要担心返回的内容 - 它知道这将是一个有效的数值。)
  • @kenWhite 很难解释 onespell 到底是什么,但是百分比、加号和数量的类型应该是整数。 db 很大,要扔掉所有 0 都需要一些时间。如果它为空,则不使用百分比,加上数量,因此我认为 0 来压缩错误。我希望有一种方法可以在一行中添加类似 oneSpell.plus := if(null)then 0 else .... 的 if 语句,但我认为有一种方法可以做到这一点,是的,它是一个 adoquery,使用 access db。但如果可能的话,宁愿在这些代码行中做到这一点......曾经是一个巨大的数据库......
  • 再一次,FQuery 是什么?是TADOQuery吗? (这有所不同,因为可用的内容因类而异。另外,如果是TADOQuery,是否真的有多个记录集?如果没有,则无需使用FQuery.RecordSet,这样更容易。 )
  • @KenWhite 抱歉,我将其添加到最后一条评论中,但在您阅读后可能会出现问题,但是 FQuery 是一个 TADOQuery,是的,有多个记录集。就像在某些代码中一样,您可以使用 ?并保持在同一行
  • 我不知道如何避免它,因为你没有解释为什么你的查询首先有多个记录集。大多数 SQL 查询不会返回超过一个,除非您使用 TADOCommand 而不是在一个脚本中执行一系列 SELECT 语句。 (我假设您知道多个 recordsets 与多个 records(行)不同。)

标签: delphi delphi-xe2 ado


【解决方案1】:

这是常见的方法:

提取重复部分以保持代码干燥并增加可读性

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,因为我未能在自己的答案中继续得出合乎逻辑的结论 - 我停了一级。 :$
  • @SirRufo at if value = NULL then 我收到错误,即未定义 NULL。
【解决方案2】:

因为您使用的是ADOQuery.Recordset,它返回对底层_Recordset 的引用(不是来自Delphi,而是直接是ADO 对象引用),您的选择仅限于ADO 直接支持的那些。 Delphi 的 TADOQuery 具有封装方法,可以隐藏在较低级别处理 ADO 的许多复杂性,而使用 Recordset 会严重限制您的选择。

AFAICT,在一行中执行此操作的唯一方法是使用 long line,它同时利用 VarIsNull 函数和 Math.IfThen 函数(如果您使用,则默认为 0省略AFalse 参数):

OneSpell.PerCent := IfThen(not VarIsNull(FQuery.Recordset.Fields[ DB_FLD_PER_CENT].Value), FQuery.Recordset.Fields[DB_FLD_PER_CENT].Value);

您可以使用中间的OleVariant 变量(仍然需要Math 单元)以更易读的方式在两行中执行此操作:

var
  Val: OleVariant;
...
  Val := FQuery.Recordset.Fields[DB_FLD_PER_CENT].Value;
  OneSpell.PerCent := IfThen(not VarIsNull(Val), Val);

最简单的替代方案(无论如何,从 Delphi 代码的角度来看)是在您的 SQL 语句本身中处理这个问题,使用 IsNullCoalesce 或 MS Access 中的等效项,因此您不必担心它在您的应用程序代码中;您可以简单地访问 Value 并知道它包含一个整数。

实际上,最简单的替代方法是根本不使用RecordSet,如果您实际上不需要这样做(因为您的TADOCommand 结果中有多个记录集)。如果您只是直接使用 Delphi 的 TADOQuery,您可以简单地使用原生的 TField.AsXXX 属性,它会自动为您处理转换:

OneSpell.PerCent := FQuery.Fields[DB_FLD_PER_CENT].AsInteger; // Or AsFloat

【讨论】:

  • 我认为这是一个很好的答案,使用.AsInteger 是最简单的。但直到我记得为什么我使用记录集..我将使用数学单元走这条路
【解决方案3】:

停止在 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

【讨论】:

  • 这仅在字段为聚合或计算字段时不起作用。
【解决方案4】:

如果您知道始终希望 Null 被解释为 false 布尔值、0 数值或空字符串,则可以设置 System.Variants.NullStrictConvert = false。

(严格来说,对于 Null 到字符串的转换,将采用 System.Variants.NullAsStringValue 的值)。

【讨论】:

    【解决方案5】:

    将尝试展示另外两个变体,它们在某种程度上是开箱即用的。

    1:使用TDataSet原生访问

    OneSpell.Plus := TCardPlus  ( FQuery.FieldByName( 'DB_FLD_PLUS' ).AsInteger ); 
    

    OneSpell.Plus := TCardPlus  ( FQuery.Fields[ 2 ].AsInteger ); 
    

    假设“DB_FLD_PLUS”是您查询中的第三列。 TField.AsInteger 为空 (NULL) 列返回零。

    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;
    

    【讨论】:

    • As<Type> 转换对于 TCustomADODataSet 后代来说是最好的,因为如果基础字段是 NULL,数字类型转换返回 0。问题是 OP 访问的不是 ADO 数据集,而是 ADO 记录集,其字段为Field 类型,值返回为OleVariant
    • @tlama 请不要使用尖括号:我花了很长时间试图弄清楚在哪里使用泛型 :-D 由于数据集是记录集的包装器,因此阅读该特定文件会有什么问题通过 TField 列?
    • 对此感到抱歉:-)我已从已删除的答案中复制了它。好吧,这取决于此处使用的记录集访问的真正目的。这可能是由于性能原因(因为它提供了对 ADO 记录集接口的引用),或者可能只是一个错误。或者只是访问通过NextRecordset 方法获取的下一个数据集(声明为变量)的展示。很难说。如果是错误的,AsType 转换将恕我直言是最优雅的方式(它也会增加类型安全)。
    【解决方案6】:

    有趣的帖子和有趣的答案,无论如何,没有提到的内容,至少在所有答案中都很清楚,并且可能看起来很明显,但对于 Delphi/Firedac 的新手来说,你需要包括的并不是很多(添加到模块的使用)接口 SYSTEM.VARIANTS 以询问 datafield.value = NULL 还是 NULL。

    【讨论】:

      猜你喜欢
      • 2015-02-08
      • 1970-01-01
      • 1970-01-01
      • 2021-08-16
      • 1970-01-01
      • 2023-02-25
      • 2015-03-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多