【发布时间】:2020-10-15 16:28:23
【问题描述】:
一些背景资料:
要创建的模型适用于 postgresql 数据库中现有的 partitioned 表。性能问题的原因是请求父表并不理想。我们创建了一个自定义数据库函数来从子表中获取数据,并根据参数通过 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;
$$;
【问题讨论】:
-
用你的函数创建一个实际的第二个表不是更容易(例如,使用数据库触发器保持同步),然后像你一样在 Eloquent 中使用第二个表作为数据源有桌子吗?
-
我不确定你的意思。我认为您建议为数据创建一种缓存表。问题是使用的 ID 取决于用户权限。因此,如果权限被修改,我将不得不为每个用户创建一个自己的表集并创建新的表。那太复杂了。如何处理参数?
-
是的,这就是我的建议。您能否仅将用户 ID /“自定义 ID”添加到缓存表中的数据集 - 正如您所说的那样 - 缓存表并让您的应用程序代码只让用户访问他应该有权访问的记录?我知道这并不能真正回答你的问题,我的意思是它可能被认为太复杂了
-
访问数据是一个性能问题。数据库必须搜索所有子表。我的习惯在桌子上。我能想到的唯一选择是创建一个自定义模型来合并所有查询
-
您是否为要查询的属性添加了索引?函数会比查询产生更快的结果,这听起来有点奇怪。如果您没有(!)有适当的临时缓存(如其他表),则更是如此。您能否将您的数据库架构添加到问题中,以便我们可以从那里开始?
标签: php postgresql eloquent model