2014-01-11 5 views
2

я получаю значение из БД, и когда значение NULL я получаю сообщение об ошибкекак проверить, если нулевое значение в одной строке

Не удалось преобразовать вариант типа (нуль) в тип (Integer)

Если я заполняю в БД с вместо ничего (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 затем сделать его ? Или это нужно сделать через db.

FQuery является adoquery и дб доступ

Я знаю, что я мог сделать

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

Но я хочу сделать это в одной строке для каждого значения onespell

+1

Что такое 'OneSpell'? Какой тип 'PerCent',' Plus' и 'Quantity'? Является ли 'FQuery'' TADOQuery' или что-то еще? Можете ли вы быть более конкретными, поэтому мы можем попытаться помочь вам? (И да, если вы хотите 0 вместо NULL, самым простым решением для сокращения кода является выполнение этого в самом SQL с использованием 'IsNull()' или 'Coalesce()' или любого эквивалента в любой неуказанной системе базы данных , потому что тогда вашему приложению не нужно беспокоиться о том, что возвращается - он знает, что это будет действительное числовое значение.) –

+0

@kenВысоко говоря, было трудно объяснить, что именно есть, но тип для проценты, плюс и количество должны быть целыми. db огромен, и он бросается, и все 0 займет некоторое время. и если его значение null, процент, плюс, количество не используется, поэтому я решил 0 сжать ошибку. Я надеялся на способ добавить, если оператор в одной строке будет похож на этот 'oneSpell.plus: = if (null), затем 0 else ....', но думал, что есть способ сделать это да его adoquery, используя доступ дб. но предпочел бы сделать это в этих строках кода, если возможно ... однажды agian огромный db .. –

+1

Еще раз, что такое 'FQuery'?Это «TADOQuery»? (Это имеет значение, потому что то, что доступно для использования, зависит от класса. Кроме того, если это «TADOQuery», есть ли на самом деле несколько наборов записей? Если нет, нет необходимости использовать 'FQuery.RecordSet', что делает его проще.) –

ответ

8

То есть общий путь:

Extract повторяющиеся части в сохранить код 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); 
+0

+1, потому что я не смог продолжить логический вывод в своем собственном ответе - я остановился на одном уровне. : $ –

+0

@SirRufo at 'if value = NULL then' i get error, который NULL не определен. –

+0

'NULL' определяется в модуле' Variants' –

6

Потому что вы используя ADOQuery.Recordset, который возвращает ссылку на базовый _Recordset (который не связан с Delphi, но является ссылкой на объект ADO напрямую), ваши варианты ограничены теми, которые ADO поддерживает напрямую , В Delphi TADOQuery есть методы-обертки, которые скрывают большую сложность работы с ADO на более низком уровне, а использование Recordset вместо этого сильно ограничивает ваши параметры.

AFAICT, единственный способ сделать это в одной строке будет с помощью длинной линии, которая использует как VarIsNull функцию и функцию Math.IfThen (который по умолчанию 0 если опустить AFalse paremeter):

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, используя IsNull или Coalesce или его эквивалент в MS Access, поэтому вам не нужно беспокоиться об этом в коде приложения ; вы можете просто получить доступ к Value и знать, что он содержит целое число.

На самом деле, альтернативный простой не использовать RecordSet вообще, если вы на самом деле не нужно делать (потому что есть несколько наборов записей в ваших TADOCommand результатов).Если вы просто использовать в Delphi TADOQuery напрямую, вы можете просто использовать собственные TField.AsXXX свойства, которые будут обрабатывать преобразования для вас автоматически:

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

Я думаю это отличный ответ, и использовать '.AsInteger' было бы проще всего. но до тех пор, пока я не вспомню, почему я использовал набор записей. Я пойду по маршруту с использованием математического блока –

2

Если вы знаете, что вы всегда будете хотеть Нулевой интерпретировано как ложное логическое, числовое значение 0 или пустая строка, вы можете установить System.Variants.NullStrictConvert = ложь ,

(Строго говоря, для преобразования Null в строку значение System.Variants.NullAsStringValue будет принято).

4

Остановить выполнение работы в коде Delphi, ваш запрос должен обработать это. Большинство базовых БД поддерживают функцию COALESCE. Поэтому в вашем запросе просто используйте: SELECT COALESCE(MyIntegerField, 0) FROM Foo. Если MyIntegerField содержит значение NULL, COALESCE возвратит первый non-NULL значение, в этом случае 0

EDIT

Просто понял, что ваш DB Бэкэнд MS Access, в этом случае следует использовать функцию IIF:

SELECT Percent= IIf([Percent] Is Null, 0, [Percent]) FROM Foo 
+0

Это не работает, если поля являются полями Aggregated или Calculated. – EMBarbosa

2

Попробует показать еще два варианта, которые выходят из коробки несколько.

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; 
+0

Листинг 'As ' будет лучшим для потомков TCustomADODataSet, так как числовое значение typecasting возвращает 0, если базовое поле является 'NULL'. Проблема в том, что OP не получает доступ к набору данных ADO, но ADO recordset, поля которого имеют значение ['Field'] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms677568 (v = vs .85) .aspx) тип и значения возвращаются как 'OleVariant'. – TLama

+0

@tlama plz не использует угловые скобки: у меня были трудные минуты, пытаясь выяснить, где использовать дженерики здесь: -D Поскольку набор данных является оберткой по набору записей, что будет проблемой при чтении этой конкретной колонки через TField? –

+0

Извините за это :-) Я скопировал его из моего удаленного ответа. Ну, это зависит от реальной цели доступа к записи, используемой здесь. Это может быть связано с особенностями производительности (поскольку в нем содержится ссылка на интерфейс набора записей ADO) или, может быть, просто ошибка. Или просто витрина для доступа к следующим наборам данных (объявленных как переменные), полученным методом ['NextRecordset'] (http://docwiki.embarcadero.com/Libraries/XE5/en/Data.Win.ADODB.TCustomADODataSet.NextRecordset). Сложно сказать. Если это по ошибке, то, что литье «AsType» было бы ИМХО самым элегантным способом (это также добавило бы безопасность типов). – TLama

Смежные вопросы