2010-03-26 6 views
7

Я получаю данные с использованием запроса в Delphi и хотел бы добавить вычисленное поле к запросу до его запуска. Вычисленное поле использует значения в коде, а также запрос, поэтому я не могу просто вычислить его в SQL.Добавление вычисленного поля в Query во время выполнения

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

Я сделал некоторые раскопки и обнаружил, что все поля DEFS созданы, но фактические поля создаются только

if DefaultFields then 
    CreateFields 

поля по умолчанию задается

procedure TDataSet.DoInternalOpen; 
begin 
    FDefaultFields := FieldCount = 0; 
    ... 
end; 

Whi ch будет указывать, что если вы добавляете поля, вы получаете только поля, которые вы добавили.

Я хотел бы, чтобы все поля в запросе были такими же, как те, которые я добавляю.

Возможно ли это, или я должен добавить все поля, которые я использую?

+0

Я не понимаю, почему вы не можете использовать значение из кода в вашем SQL .... Я построить динамические операторы SQL регулярно, что значения использования из кода в SQL .... – Leslie

+0

см мой ответ на новый способ сделать это, так как delphi Berlin –

+0

вы пытались ПОДГОТОВИТЬ запрос? Он мог бы создать fielddefs (но еще не объекты полей) –

ответ

-1

Delphi теперь имеет возможность объединить автоматические генерируемые поля и вычисляемых полей: Data.DB.TFieldOptions.AutoCreateMode перечисление типа TFieldsAutoCreationMode. Таким образом вы можете добавить свои расчетные поля во время выполнения. Франсуа написал в своем ответе, как добавить поле во время выполнения.

Различные режимы TFieldsAutoCreationMode:

  • acExclusive

    Когда нет настойчивые полей вообще, то автоматические поля создаются. Это режим "по умолчанию".

  • acCombineComputed

    Автоматические поля создаются, когда набор данных не имеет постоянных полей или есть только расчетные постоянные поля. Это удобный способ создания постоянных вычисленных полей во время разработки и позволяет набору данных создавать автоматические поля данных.

  • acCombineAlways

    Автоматические поля для полой базы данных будет созданы, когда нет настойчивых полей.

+0

Похоже, что это позволяет комбинировать расчетные поля времени проектирования и поля данных времени выполнения. Испытывали ли вы/добились успеха в своем ответе? –

+0

Это не математическая домашняя работа, где вам нужно «доказать» все. Код для создания поля во время выполнения описан в другом ответе –

+0

Ну, в документации не указано, что вы утверждаете. –

3

Вам необходимо добавить все поля в дополнение к вашему расчетному полю.

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

Delphi называет это постоянными полями по сравнению с динамическими полями. Все поля являются либо постоянными, либо динамическими. К сожалению, у вас не может быть такой комбинации.

Другое дело отметить, из документации

Persistent полей списков компонентные хранятся в приложении, и не изменения, даже если структура базы данных базового набора данных изменилось.

Итак, будьте осторожны, если позже вы добавите дополнительные поля в таблицу, вам нужно будет добавить новые поля к компоненту. То же самое с удалением полей.

Если вы действительно не хотите постоянных полей, есть другое решение. На любой сетке или элементе управления, который должен показывать вычисленное поле, вы можете настроить его. Например, многие элементы управления сетью имеют событие OnCustomDraw. Вы можете сделать свой расчет там.

10

Ничто не мешает вам создавать все поля сначала в вашем коде,
, а затем добавить ваши расчетные поля.

Вы можете использовать "взломанный типа" использовать защищенные CreateFields:

type 
    THackQuery = class(TADOQuery) 
    end; 
[...] 
    MyQuery.FieldDefs.Update; 
    THackQuery(MyQuery).CreateFields; 

или займам код из CreateFields:

MyQuery.FieldDefs.Update; 
    // create all defaults fields 
    for I := 0 to MyQuery.FieldDefList.Count - 1 do 
    with MyQuery.FieldDefList[I] do 
     if (DataType <> ftUnknown) and not (DataType in ObjectFieldTypes) and 
     not ((faHiddenCol in Attributes) and not MyQuery.FIeldDefs.HiddenFields) then 
     CreateField(Self, nil, MyQuery.FieldDefList.Strings[I]); 

затем создать вычисляемые поля:

MyQueryMyField := TStringField.Create(MyQuery); 
    with MyQueryMyField do 
    begin 
    Name := 'MyQueryMyField'; 
    FieldKind := fkCalculated; 
    FieldName := 'MyField'; 
    Size := 10; 
    DataSet := MyQuery; 
    end; 
+0

В случае, если вы подклассифицируете TQuery или другой тип TDataset, вам не нужно «взломать» доступ к защищенным полям. Этот ответ правильный путь, когда вы (а) добавление вычисленного поля в код, и (б) все дело в коде, например, вы пишете пользовательский запрос или компонент целиком в коде. –

1

Если вы знаете, что ваши расчетные имена полей в runti меня, вы можете использовать что-то подобное.

var 
initing:boolean; 

procedure TSampleForm.dsSampleAfterOpen(
    DataSet: TDataSet); 
var 
i:integer; 
dmp:tfield; 
begin 
if not initing then 
try 
    initing:=true; 
    dataset.active:=false; 
    dataset.FieldDefs.Update; 
    for i:=0 to dataset.FieldDefs.Count-1 do 
    begin 
    dmp:=DataSet.FieldDefs.Items[i].FieldClass.Create(self); 
    dmp.FieldName:=DataSet.FieldDefs.Items[i].DisplayName; 
    dmp.DataSet:=dataset; 
    if (dmp.fieldname='txtState') or (dmp.FieldName='txtOldState') then 
    begin 
    dmp.Calculated:=true; 
    dmp.DisplayWidth:=255; 
    dmp.size:=255; 
    end; 
    end; 
    dataset.active:=true; 
finally 
    initing:=false; 
end; 
end; 

procedure TSampleForm.dsSampleAfterClose(
    DataSet: TDataSet); 
var 
i:integer; 
dmp:TField; 
begin 
if not initing then 
begin 
for i:=DataSet.FieldCount-1 downto 0 do 
begin 
    dmp:=pointer(DataSet.Fields.Fields[i]); 
    DataSet.Fields.Fields[i].DataSet:=nil; 
    freeandnil(dmp); 
end; 
DataSet.FieldDefs.Clear; 
end; 
end; 

procedure TSampleForm.dsSampleCalcFields(
    DataSet: TDataSet); 
var 
tmpdurum,tmpOldDurum:integer; 
begin 
    if not initing then 
    begin 
     tmpDurum := dataset.FieldByName('state').AsInteger; 
     tmpOldDurum:= dataset.FieldByName('oldstate').AsInteger; 
     dataset.FieldByName('txtState').AsString := State2Text(tmpDurum); 
     dataset.FieldByName('txtOldState').AsString := State2Text(tmpOldDurum); 
    end; 
end; 

procedure TSampleForm.btnOpenClick(Sender: TObject); 
begin 
if dsSample.Active then 
    dsSample.Close; 
dsSample.SQL.text:='select id,state,oldstate,"" as txtState,"" as txtOldState from states where active=1'; 
dsSample.Open; 
end;