如何在 FireDAC 中使用 TFDSQLiteFunction 添加自定义聚合 SQLite 函数?

问题描述 投票: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();

实际上,我需要实现标准差和其他一些统计函数,但为了简单起见,我们使用“总和的平方根”。

基于“Using SQLite with FireDAC”tutorial(“自定义函数”部分),我添加了以下代码:

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
循环来汇总所有值?

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

您误解了

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 note sure if FireDAC provides a nicer wrapper for this!
    double* sum = static_cast<double*>(sqlite3_aggregate_context(AOutput->Handle, sizeof(double)));
    if (!sum)
    {
        AOutput->ErrorCode = ...;
        AOutput->ErrorText = ...;
    }
    else
    {
        *sum += AInputs->Inputs[0]->AsFloat;
    }
}

void __fastcall TForm1::FDSQLiteFunction1Finalize(TSQLiteFunctionInstance *AFunc,
          TObject *&AUserData)
{
    // I'm note sure if FireDAC provides a nicer wrapper for this!
    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.