CREATE TEMP TABLE val (val_id int PRIMARY KEY, val text);
INSERT INTO val(val_id, val) VALUES
( 1, 'foo')
, ( 2, 'bar')
, (317, 'baz');
CREATE FUNCTION f_val(_id int)
RETURNS text LANGUAGE sql STABLE PARALLEL RESTRICTED AS
'SELECT val FROM val WHERE val_id = $1';
SELECT f_val(2); -- returns 'baz'
CREATE TABLE vars (
id INT NOT NULL PRIMARY KEY DEFAULT 1,
zipcode INT NOT NULL DEFAULT 90210,
-- etc..
CHECK (id = 1)
);
创建一个动态函数,该函数加载表的内容,并使用它执行以下操作:
重新创建另一个单独的静态不可变getter函数。
CREATE FUNCTION generate_var_getter()
RETURNS VOID AS $$
DECLARE
var_name TEXT;
var_value TEXT;
new_rows TEXT[];
new_sql TEXT;
BEGIN
FOR var_name IN (
SELECT columns.column_name
FROM information_schema.columns
WHERE columns.table_schema = 'public'
AND columns.table_name = 'vars'
ORDER BY columns.ordinal_position ASC
) LOOP
EXECUTE
FORMAT('SELECT %I FROM vars LIMIT 1', var_name)
INTO var_value;
new_rows := ARRAY_APPEND(
new_rows,
FORMAT('(''%s'', %s)', var_name, var_value)
);
END LOOP;
new_sql := FORMAT($sql$
CREATE OR REPLACE FUNCTION var_get(key_in TEXT)
RETURNS TEXT AS $config$
DECLARE
result NUMERIC;
BEGIN
result := (
SELECT value FROM (VALUES %s)
AS vars_tmp (key, value)
WHERE key = key_in
);
RETURN result;
END;
$config$ LANGUAGE plpgsql IMMUTABLE;
$sql$, ARRAY_TO_STRING(new_rows, ','));
EXECUTE new_sql;
RETURN;
END;
$$ LANGUAGE plpgsql;
WITH const AS (
select 5 as MY_ID,
'2022-03-1'::date as MY_DAY)
SELECT u.user_group,
COUNT(*),
const.MY_DAY
FROM users u
CROSS JOIN const
WHERE 1=1
GROUP BY u.user_group, const.MY_ID, const.MY_DAY
示例包含的字段比OP多,但这有助于更多的访问者查找主题。
不带分组依据:
WITH const AS (
select 5 as MY_ID)
SELECT u.* FROM users u
CROSS JOIN const
WHERE u.id = const.MY_ID
感谢@GordonLinoff
没有GROUP BY且没有列名冲突:
WITH const AS (
select 5 as MY_ID)
SELECT users.* FROM users
CROSS JOIN const
WHERE id = MY_ID
CREATE DATABASE mydb;
CONNECT TO mydb
CREATE ROLE myrole;
ALTER DATABASE mydb
SET app.settings.bla = 1::text;
ALTER ROLE myrole
IN DATABASE mydb
SET app.settings.bla = 2::text;
SET ROLE myrole;
SET app.settings.bla TO 3::text;
SELECT current_setting('app.settings.bla', true); -- '3'
SELECT pg_db_role_setting('app.settings.bla'); -- '1'
SELECT pg_db_role_setting('app.settings.bla', current_user); -- '2'
如果您愿意,测试例程可以更详细一些:
CREATE OR REPLACE PROCEDURE ext.test__pg_db_setting()
LANGUAGE plpgsql
SET "plpgsql.check_asserts" TO 'true'
SET "pg_readme.include_this_routine_definition" TO 'true'
AS $procedure$
begin
execute 'ALTER DATABASE ' || current_database()
|| ' SET pg_safer_settings.test_pg_db_setting = ''foo''';
assert pg_db_setting('pg_safer_settings.test_pg_db_setting') = 'foo';
set pg_safer_settings.settings.test_pg_db_setting = 'bar';
assert pg_db_setting('pg_safer_settings.test_pg_db_setting') = 'foo';
assert pg_db_setting('pg_safer_settings.unknown_setting') is null;
create role __test_role;
execute 'ALTER ROLE __test_role IN DATABASE ' || current_database()
|| ' SET pg_safer_settings.test_pg_db_setting = ''foobar''';
assert pg_db_setting('pg_safer_settings.test_pg_db_setting', '__test_role') = 'foobar';
assert pg_db_setting('pg_safer_settings.test_pg_db_setting') = 'foo';
raise transaction_rollback;
exception
when transaction_rollback then
end;
$procedure$
pg_safer_settings还提供了@Brev his answer提出的模式的更复杂的变体。
7条答案
按热度按时间kulphzqa1#
这个问题以前曾经被问过(How do you use script variables in PostgreSQL?),但是,我有时会使用一个技巧来进行查询:
也就是说,我定义了一个名为const的CTE,其中包含了定义的常量。然后,我可以在任何级别将其交叉连接到查询中,次数不限。我发现,当我处理日期时,这一点特别有用,因为我需要跨许多子查询处理日期常量。
to94eoyn2#
PostgreSQL没有像MySQL或Oracle那样的内置方式来定义(全局)变量。(使用"customized options"有一个有限的解决方案)。根据您的需要,还有其他方式:
对于 * 一个 * 查询
您可以在CTE中的查询顶部提供值,如已经提供的@Gordon。
全局持久常量:
您可以为此创建一个简单的
IMMUTABLE
函数:(Parallel safety设置仅适用于Postgres 9.6或更高版本。)
它必须存在于一个对当前用户 * 可见 * 的模式中,即在相应的
search_path
中。默认情况下,类似于模式public
。如果安全性是一个问题,请确保它是search_path
中的第一个模式,或者在调用中对其进行模式限定:对数据库中的所有用户(允许访问方案
public
的用户)可见。* 当前会话 * 有多个值:
由于plpgsql在创建时检查表是否存在,因此在创建函数之前,您需要创建一个(临时)表
val
-即使在会话结束时删除了临时表,而函数仍在继续。如果在调用时未找到基础表,函数将引发异常。默认情况下,临时对象的当前模式在
search_path
的其他模式之前-如果没有明确指示的话。您不能从search_path
中 * 排除 * 临时模式,但是您可以将其他模式放在前面。夜晚的邪恶生物(拥有必要的特权)可能会修改
search_path
,并在前面放置另一个同名的对象:这并不是一个很大的威胁,因为只有特权用户可以修改全局设置,其他用户只能在他们自己的会话中这样做,请参考手册中关于使用
SECURITY DEFINER
创建函数的相关章节。硬连接模式通常更简单、更快:
包含更多选项的相关答案:
vsikbqxv3#
除了Gordon和Erwin已经提到的合理选项(临时表、常量返回函数、CTE等)之外,您还可以(ab)使用PostgreSQL GUC机制来创建全局、会话和事务级变量。
请参见this prior post,其中详细说明了该方法。
我不建议将此用于一般用途,但它可能在小范围的情况下有用,如链接问题中提到的情况,即发帖者希望找到一种向触发器和函数提供应用程序级别用户名的方法。
mhd8tkvw4#
我找到了这个解决方案:
zdwk9cvp5#
我已经发现了一种混合的可用方法是最好的:
generate_var_getter()
,并重新创建不可变的var_get()
函数。一个二个一个一个
现在,您可以轻松地将变量保存在表中,同时还可以对它们进行快速的不可变访问。
bvjxkvbb6#
当查询使用“GROUP BY”时:
示例包含的字段比OP多,但这有助于更多的访问者查找主题。
不带分组依据:
感谢@GordonLinoff
没有GROUP BY且没有列名冲突:
elcex8rz7#
如果你想在会话中保持常量,并且不想使用表,你可以为一个特定的数据库或角色设置自定义设置,这样的设置可以在会话或事务级别被覆盖,但是直接访问
pg_db_role_settings
系统目录很容易理解:这个函数是😉从我的
pg_safer_settings
扩展中复制粘贴过来的。下面是一个示例(摘自
pg_safer_settings
自述文件),说明如何使用它:如果您愿意,测试例程可以更详细一些:
pg_safer_settings
还提供了@Brev his answer提出的模式的更复杂的变体。