Nux*_*Nux 3 postgresql dynamic-sql plpgsql search-path
我想为 PG SQL 数据库中的模式创建一个更新函数。下面是一个测试功能。它不起作用,因为它永远不应该发出通知,但在运行时会这样做test_schema_update('second')。
CREATE OR REPLACE FUNCTION test_schema_update(my_schema_name VARCHAR(200))
RETURNS void AS
$__marker__$
DECLARE
actualValue varchar(1000);
testValue varchar(1000);
BEGIN
EXECUTE 'SET search_path TO ' || quote_ident(my_schema_name);
testValue := (SELECT max(value) FROM setting WHERE settingkey = 'libraryname');
EXECUTE ('SELECT max(value) FROM setting WHERE settingkey = ''libraryname''')
INTO actualValue;
IF (actualValue != testValue)
THEN
RAISE NOTICE '% != %', actualValue, testValue;
RAISE INFO 'Schema was: %', current_schema();
END IF;
RESET search_path;
END;
$__marker__$ LANGUAGE plpgsql;
test_schema_update('first');
test_schema_update('second');
Run Code Online (Sandbox Code Playgroud)
问题是 PG SQL 似乎SELECT每个会话只分析一次语句,然后将表固定到特定模式。有趣的是你会得到Schema was: second.
那么有没有办法重置SELECT语句分析或其他一些方法来解决这个问题?
旁注:所有模式创建函数 ( ALTER TABLE, CREATE TABLE...) 工作正常。似乎只有数据操作函数会受到影响 ( SELECT, INSERT, UPDATE)。
前:
IF (
SELECT max(id) FROM dimtime
)
THEN
INSERT INTO dimtime SELECT * FROM public.src_dimtime;
END IF;
Run Code Online (Sandbox Code Playgroud)
后:
EXECUTE ('
SELECT max(id) FROM dimtime
')
INTO testInt;
IF (testInt IS NULL)
THEN
EXECUTE 'INSERT INTO dimtime SELECT * FROM public.src_dimtime';
END IF;
Run Code Online (Sandbox Code Playgroud)
编辑:该问题出现在 PostgreSQL 9.2 中,但似乎没有出现在 9.3 中。也许它是固定的?
这种行为是可以预料的。原因是 PL/pgSQL对 SQL 语句使用计划缓存,内部使用标准的预准备语句。
由于每个表达式和 SQL 命令首先在函数中执行,PL/pgSQL 解释器创建一个准备好的执行计划(使用 SPI 管理器
SPI_prepare和SPI_saveplan函数)。对该表达式或命令的后续访问重复使用准备好的计划。
这也是为什么 plpgsql 函数在复杂操作中通常比普通 SQL 函数更快的原因:
准备好的语句在session的生命周期内保存,而不仅仅是事务(但在底层对象更改时失效,这对于并发访问是安全的)。文档再次:
一旦 PL/pgSQL 为函数中的特定命令制定了执行计划,它将在数据库连接的生命周期中重用该计划。这通常是性能的胜利,但如果您动态更改数据库架构,它可能会导致一些问题。
大胆强调我的。
如果你想“改变”一个表名的模式,你真的会引用一个完全不同的表,并且需要使用动态 SQL with EXECUTE,它每次都会生成一个新计划(具有所有优点和缺点):
因为 PL/pgSQL 以这种方式保存执行计划,直接出现在 PL/pgSQL 函数中的 SQL 命令必须在每次执行时引用相同的表和列;也就是说,您不能在 SQL 命令中使用参数作为表或列的名称。为了绕过这个限制,你可以使用 PL/pgSQL
EXECUTE语句构造动态命令 ——代价是在每次执行时构造一个新的执行计划。
阅读手册中的参考章节。它相当全面。
您添加的代码示例不需要动态 SQL,并且单个语句会更快:
INSERT INTO dimtime -- you may want list columns
SELECT * -- here as well
FROM public.src_dimtime
WHERE NOT EXISTS (SELECT 1 FROM dimtime);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5378 次 |
| 最近记录: |