vol*_*ron 4 postgresql database-design execution-plan view
我正在尝试修改共享开发数据库,供开发团队用于应用程序开发/测试。
大多数集体工作(表名、视图等)都存储在公共环境中模式中,但我已经为每个用户设置了模式以用作临时空间。然而,真正的目标是用户使用其架构中的对象(如果存在),然后依赖其他对象,就像今天使用 search_path 的方式一样。
用一个例子可能会更好地描述这一点。假设团队正在开发一个汽车维修应用程序的数据库,其中包含一些表和视图:
public.automobiles
public.parts
public.inventory
public.mechanics
public.schedule
public.v_repairs -- view that joins fields from all tables above
Run Code Online (Sandbox Code Playgroud)
这非常有效,但假设开发人员(例如 Sally)想要测试一项新功能,用她自己的数据集来检查视觉反馈或阈值测试。她在自己的架构中创建了一个表sally.schedule
。因为默认的search_path类似于"$user",public
,所以她创建的任何简单查询都会在公开之前首先检查她的架构。例如:
SELECT * FROM schedule LEFT JOIN mechanics USING(mechanic_id);
Run Code Online (Sandbox Code Playgroud)
当 sally 连接到数据库时,这将使用 public.mechanics 和 sally.schedule。这正是预期的用途,但是在保存视图时,它通过插入架构来完全限定表名称。因此,如果将上面相同的查询创建为公共模式中的视图,它将如下所示:
SELECT * FROM public.schedule LEFT JOIN public.mechanics USING(mechanic_id);
Run Code Online (Sandbox Code Playgroud)
search_path 的魔力被否定了。当Sally连接到数据库调用视图时(SELECT * FROM v_mechanics_schedule
) 时,它会忽略她创建的 sally.schedule 表,而只使用公共表。
有没有办法在保存视图时不让 Postgres 存储表/视图对象的架构名称?
注意:这是我正在研究的新事物,但我从未真正需要过它,因为开发人员通常可以克隆应用程序、复制数据库并在自己的沙箱环境中工作。不需要一些巧妙的协作模式设置
不,没有办法。我认为人们对事情的运作方式存在轻微的误解。
与将函数体存储为字符串(后期绑定!)的函数不同,视图解析查询并且根本不存储原始文本。更新:从 Postgres 14 开始,还有 SQL 标准函数(带有),它们也会在创建时解析主体!看:BEGIN ATOMIC
所有标识符都根据当前解析search_path
,并且仅保存其内部 OID(早期绑定!)。这也是为什么SELECT *
总是在该时间点解析为列列表的原因VIEW
。
当查看 a 的定义时,您在 pgAdmin 或其他客户端中看到的VIEW
是重新设计的 SQL 字符串。永远public.schedule
是并且永远是public.schedule
sally.schedule
sally.schedule
(除非您重命名表或架构选择显示的(模式限定的)名称,以便标识符对于当前搜索路径是明确的。
这也会显示您何时更改列、表或架构的名称。该视图一直有效,因为它不依赖于这些属性。定义的显示是动态调整的。
如果您想要后期绑定,您可以使用函数而不是视图,并默认为当前搜索路径(可以SET
函数范围的搜索路径来精确避免这种影响。)
所以而不是:
CREATE VIEW v_mechanics_schedule AS
SELECT *
FROM schedule s -- use aliases for short qualified column names in display
LEFT JOIN mechanics m USING (mechanic_id);
Run Code Online (Sandbox Code Playgroud)
如果没有架构限定,表名称将根据创建时search_path
的当前名称进行解析当前名称进行解析。
你可以:
CREATE FUNCTION f_mechanics_schedule()
RETURNS TABLE (...) -- spell out columns
LANGUAGE SQL AS
$func$
SELECT * FROM schedule s -- typically error prone
LEFT JOIN mechanics m USING(mechanic_id);
$func$
Run Code Online (Sandbox Code Playgroud)
现在,表名是根据search_path
执行时间解析的。请注意,这不适用于返回类型 ( RETURNS TABLE (...)
),它是在创建时确定的。因此,这个技巧仅适用于兼容的表 - 查询必须返回相同的列列表,只有数据类型和列数很重要,列名被忽略。只有子句中定义的列名称RETURNS
在函数外部可见。
这也是为什么SELECT *
在函数体中返回值通常是不可靠的。如果更改基础表的定义,该函数会中断:定义的返回类型保持不变,但SELECT *
解析为不同的列列表...
OTOH,如果您将函数声明为RETURNS SETOF some_table
(不同的语法变体),则会注册函数依赖项,并且函数的返回类型绑定到表定义,因此SELECT * FROM some_table
更有意义。
SECURITY DEFINER
这也是为什么在不修复以下问题的情况下拥有函数是不安全的原因search_path
:任何具有TEMP
权限的用户都可以创建一个隐藏其他表的临时表,因为pg_temp
默认情况下在搜索路径中排在第一位...
相关:
您确实需要了解底层机制才能发挥作用。
归档时间: |
|
查看次数: |
721 次 |
最近记录: |