php - 如何创建一个以数据库函数为源的雄辩模型?
问题描述
一些背景资料:
要创建的模型适用于 postgresql 数据库中的现有分区表。性能问题的原因是请求父表并不理想。我们创建了一个自定义数据库函数来从子表中获取数据,并根据参数通过 union all 连接结果。参数的数据由用户权限定义,因此在创建模型时就知道 id。背后的逻辑严重依赖模型和关系,因此原始查询会造成很大的麻烦。
目前我使用父表来获取数据,但请求需要 2-3 秒。调用子表时,结果低于 100 毫秒。所以我需要一种从多个子表中获取数据的可能性。
对于这个问题:
如何将现有功能设置为模型的基础?
我试图将函数名放在 DB::raw 中,但该函数仍然被转义:SELECT *FROM "partionedTableTestGetData(1,2,3)"
。我需要用作模型数据源的函数。该函数直接查询子表并使用 union all 来构建结果集。
不需要写入此模型。
use Illuminate\Support\Facades\DB;
public static function boot()
{
$oUser = Auth::user();
$aIds = $oUser->getCustomIds();
//some sanitation here
$this->table = DB::raw('partionedTableTestGetData('.implode(',', $aIds ).')';
static::addGlobalScope(new CustomScope('id-field'));
}
表的数据库模式:
- public.parent_table -> parent table
- parent_table.parent_table_123 -> child table of parent_table with all data of company with id 123
- parent_table.parent_table_321 -> child table of parent_table with all data of company with id 321
... about 600 other tables like this ...
以下函数用于获取 child_table 数据:
create function partionedTableTestGetData(companyids text) returns SETOF parent_table
language plpgsql
as $$
DECLARE
aiIDs BIGINT[];
iID BIGINT;
sSql TEXT;
sSqlCondition TEXT;
bFirst BOOLEAN;
bParent BOOLEAN;
BEGIN
bFirst = TRUE ;
bParent = FALSE ;
aiIDs = string_to_array(sSzenarioIDs, ',');
sSql = '';
sSqlCondition = '';
FOREACH iID IN ARRAY aiIDs
LOOP
IF EXISTS (SELECT table_name FROM information_schema.tables WHERE table_name='parent_table' || iID AND table_schema = 'parent_table')
THEN
IF bFirst IS FALSE
THEN
sSql = sSql || ' UNION ALL ';
END IF;
bFirst = FALSE;
sSql = sSql || format('SELECT * FROM parent_table.parent_table_%s %s', iID, sSqlCondition);
ELSE bParent = TRUE;
END IF;
END LOOP;
IF bParent IS TRUE OR sSql = ''
THEN
IF bFirst IS FALSE
THEN
sSql = sSql || ' UNION ALL ';
END IF;
bFirst = FALSE;
sSql = sSql || format('SELECT * FROM ONLY public.parent_table %s', sSqlCondition);
END IF;
RAISE NOTICE '%', sSql;
IF sSql <> ''
THEN RETURN QUERY EXECUTE sSql;
END IF;
RETURN;
END;
$$;
解决方案
尽管感觉缓存表是处理此问题的首选方式。这应该可以解决您的问题。
$items = \DB::connection()->select(
'SELECT * FROM partionedTableTestGetData(:ids)',
['ids' => implode(',', $aIds)]
);
如果您希望 Eloquent 模型被通过查询获取的数据水合,您可以使用以下内容:
Model::hydrate($items)->all() // returns Model[]
推荐阅读
- php - 我需要有关如何返回某些值的帮助
- docker - 如何立即保存在多阶段 docker 中创建的 dockers 图像?
- python - Windows 命令提示符下 Python 3.6 中的模块未找到错误
- unity3d - EmguCV 导致 Unity 崩溃
- python - Python AttributeError 实例没有属性
- asp.net - 如何显示存储过程中的单个结果以进行查看
- game-engine - PC游戏开发之路
- java - 是否为每个类实例创建 Cglib 方法拦截器?
- excel - Excel - 2 个工作表中的 2 个表(如果 2 个表中的 2 个单元格匹配,则在表 2 中查找单元格值)
- llvm - libunwind 是在 libcxxabi 中实现 abi 的吗?