如何使用 TFDSQLiteFunction 添加自定义聚合函数?

问题描述 投票:0回答:1

我想创建一个自定义函数以这种方式使用:

FDQuery1->SQL->Text = "SELECT SQRT_SUM(FIELD_1) FROM TABLE_1 WHERE ID = 1";
FDQuery1->Open();

ShowMessage(Form1->FDQuery1->Fields->Fields[0]->AsFloat);

FDQuery1->Close();

我需要标准差和其他统计函数,但为了简单起见,我们使用“总和的平方根”。基于 “将 SQLite 与 FireDAC 结合使用”教程我添加了:

void __fastcall TForm1::FDSQLiteFunction1Calculate(TSQLiteFunctionInstance *AFunc,
          TSQLiteInputs *AInputs, TSQLiteOutput *AOutput, TObject *&AUserData)

{
    double sum = 0;
    for (long i = 0; i < AInputs->Count; i++) sum += AInputs->Inputs[i]->AsFloat;
    AOutput->AsFloat = sqrt(sum);
}

FDSQLiteFunction1->DriverLink = FDPhysSQLiteDriverLink1;
FDSQLiteFunction1->FunctionName = 'SQRT_SUM';
FDSQLiteFunction1->Aggregated = true;
FDSQLiteFunction1->ArgumentsCount = 1;
FDSQLiteFunction1->OnCalculate = FDSQLiteFunction1Calculate;
FDSQLiteFunction1->Active = true;

但是该函数是为每一行调用的,而不是一次,除非使用全局变量,否则我无法获得我想要的值。如何执行

for
循环来对所有值求和?

c++ sqlite firedac
1个回答
0
投票

如何进行

for
循环来汇总所有值?

你不知道。您误解了

OnCalculate
事件在您的情况下如何运作。

对于非聚合函数,

OnCalculate
事件确实按照您的预期工作。它仅被调用 1 次,其中
AInputs
包含所有输入值。 该事件必须输出最终计算值。

但是,对于 aggregate 函数,当 SQLite 迭代正在聚合的数据集时,每个步骤(即记录)都会调用

OnCalculate
事件,因此
AInputs
将仅包含记录的输入值在当前步骤中。
OnCalculate
事件尚无法返回最终计算值,因此您必须在每个步骤中保留当前计算,然后在迭代完成后使用
OnFinalize
事件计算最终值。

请参阅 SQLite 的文档 - 应用程序定义的 SQL 函数 ,特别是关于 回调 的部分。

尝试更多类似这样的事情:

void __fastcall TForm1::FDSQLiteFunction1Calculate(TSQLiteFunctionInstance *AFunc,
    TSQLiteInputs *AInputs, TSQLiteOutput *AOutput, TObject *&AUserData)
{
    // I'm not sure if FireDAC provides a nicer wrapper for this call!
    double* sum = static_cast<double*>(sqlite3_aggregate_context(AOutput->Handle, sizeof(double)));
    if (!sum)
    {
        AOutput->ErrorCode = ...;
        AOutput->ErrorText = ...;
        return;
    }
    //

    *sum += AInputs->Inputs[0]->AsFloat;
}

void __fastcall TForm1::FDSQLiteFunction1Finalize(TSQLiteFunctionInstance *AFunc,
    TObject *&AUserData)
{
    // I'm not sure if FireDAC provides a nicer wrapper for this call!
    double* sum = static_cast<double*>(sqlite3_aggregate_context(AOutput->Handle, 0));
    //

    AFunc->Output->AsFloat = sum ? sqrt(*sum) : 0.0;
}

FDSQLiteFunction1->DriverLink = FDPhysSQLiteDriverLink1;
FDSQLiteFunction1->FunctionName = 'SQRT_SUM';
FDSQLiteFunction1->Aggregated = true;
FDSQLiteFunction1->ArgumentsCount = 1;
FDSQLiteFunction1->OnCalculate = FDSQLiteFunction1Calculate;
FDSQLiteFunction1->OnFinalize = FDSQLiteFunction1Finalize;
FDSQLiteFunction1->Active = true;
© www.soinside.com 2019 - 2024. All rights reserved.