如何用TFDSQLiteFunction实现正确的自定义聚合函数(如SQRT_SUM)?
自定义SQLite聚合函数(FireDAC)实现指南:以SQRT_SUM为例
需求场景
需要在FireDAC中实现自定义SQLite聚合函数(示例为SQRT_SUM,实际需求为标准差这类统计函数),用于如下SQL查询:
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();
遇到的问题
参考官方教程实现的代码会被逐行调用,若不使用全局变量则无法正确累加所有行的数值,得不到预期的聚合结果。
解决方案:利用AUserData存储中间状态
SQLite聚合函数的执行分为初始化、逐行累加、最终计算三个阶段,FireDAC通过AUserData指针提供线程安全的中间状态存储,无需依赖全局变量。
正确的代码实现
void __fastcall TForm1::FDSQLiteFunction1Calculate(TSQLiteFunctionInstance *AFunc, TSQLiteInputs *AInputs, TSQLiteOutput *AOutput, TObject *&AUserData) { // 初始化或最终计算阶段(无输入参数) if (AInputs->Count == 0) { if (AUserData == nullptr) { // 初始化:分配内存存储累加值 AUserData = new double(0.0); } else { // 最终计算:取出累加值,计算平方根并输出 double sum = *static_cast<double*>(AUserData); AOutput->AsFloat = sqrt(sum); // 释放内存,避免泄漏 delete static_cast<double*>(AUserData); AUserData = nullptr; } return; } // 逐行累加阶段(有输入参数) if (AInputs->Inputs[0]->IsValid) { double currentValue = AInputs->Inputs[0]->AsFloat; // 将当前值累加到中间存储的总和中 *static_cast<double*>(AUserData) += currentValue; } } // 函数注册代码 FDSQLiteFunction1->DriverLink = FDPhysSQLiteDriverLink1; FDSQLiteFunction1->FunctionName = L"SQRT_SUM"; FDSQLiteFunction1->Aggregated = true; FDSQLiteFunction1->ArgumentsCount = 1; FDSQLiteFunction1->OnCalculate = FDSQLiteFunction1Calculate; FDSQLiteFunction1->Active = true;
代码关键点说明
- 初始化阶段:当
AInputs->Count == 0且AUserData为空时,分配内存存储累加的初始值(0.0)。 - 逐行累加阶段:每次传入当前行的
FIELD_1值,将其累加到AUserData指向的变量中。 - 最终计算阶段:当
AInputs->Count == 0且AUserData不为空时,取出累加总和计算平方根,输出结果后释放内存。 - 线程安全:
AUserData为每个查询实例独立分配,避免全局变量的线程冲突问题。
内容的提问来源于stack exchange,提问作者Bruce
相关产品推荐
相关产品推荐

