有没有办法在PostgreSQL查询中定义命名常量?例如:
MY_ID = 5;
SELECT * FROM users WHERE id = MY_ID;
答案 0 :(得分:38)
之前已经问过这个问题(How do you use script variables in PostgreSQL?)。但是,有时我会使用查询技巧:
with const as (
select 1 as val
)
select . . .
from const cross join
<more tables>
也就是说,我定义了一个名为const的CTE,它具有在那里定义的常量。然后,我可以将其加入到我的查询中,任何级别的任意次数。我发现这在处理日期时非常有用,并且需要处理许多子查询中的日期常量。
答案 1 :(得分:37)
PostgreSQL没有内置的方法来定义(全局)变量,如MySQL或Oracle。 (使用"customized options")的解决方法有限。根据您的需要,还有其他方法:
您可以在CTE @Gordon already provided中的查询顶部提供值。
您可以为此创建一个简单的IMMUTABLE
函数:
CREATE FUNCTION public.f_myid()
RETURNS int IMMUTABLE LANGUAGE SQL AS
'SELECT 5';
它必须存在于当前用户可见的模式中,即位于相应的search_path
中。与模式public
一样,默认情况下。如果安全性存在问题,请确保它是search_path
或架构中的第一个架构 - 在您的通话中对其进行限定:
SELECT public.f_myid();
数据库中的所有用户都可以访问(允许访问模式public
)。
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 STABLE LANGUAGE SQL AS
'SELECT val FROM val WHERE val_id = $1';
SELECT f_val(2); -- returns 'baz'
由于plpgsql在创建时检查是否存在表,因此您需要在创建函数之前创建(临时)表val
- 即使临时表在会话结束时被删除,功能持续存在。如果在呼叫时未找到基础表,该函数将引发异常。
临时对象的当前架构在默认情况下在search_path
的其余部分之前出现 - 如果没有明确指示的话。您不能从search_path
排除临时架构,但您可以先放置其他架构。
夜晚的邪恶生物(具有必要的特权)可能会修改search_path
并在前面放置另一个同名的物体:
CREATE TABLE myschema.val (val_id int PRIMARY KEY, val text);
INSERT INTO val(val_id, val) VALUES (2, 'wrong');
SET search_path = myschema, pg_temp;
SELECT f_val(2); -- returns 'wrong'
这不是一个威胁,因为只有特权用户才能改变全局设置。其他用户只能为自己的会话执行此操作。要防止这种情况发生 at all ,请为您的函数和架构设置search_path
- 在调用中对其进行限定:
CREATE FUNCTION f_val(_id int)
RETURNS text STABLE LANGUAGE SQL AS
'SELECT val FROM val WHERE val_id = $1'
SET search_path = pg_temp;
或者改为使用:
... SET search_path = pg_temp, param;
这将允许您(或具有必要权限的任何人)在表param.val
中提供全局(持久)默认值...
考虑creating functions with SECURITY DEFINER手册的相关章节。
但是,这种超级安全功能不能“内联”,并且可能比使用硬连线架构的更简单的替代方案执行得更慢:
CREATE FUNCTION f_val(_id int)
RETURNS text STABLE LANGUAGE SQL AS
'SELECT val FROM pg_temp.val WHERE val_id = $1';
更多选项的相关答案:
答案 2 :(得分:6)
除了Gordon和Erwin已经提到的敏感选项(临时表,常量返回函数,CTE等)之外,您还可以(ab)使用PostgreSQL GUC机制来创建全局,会话和事务级别变量
请参阅详细显示方法的this prior post。
我不建议将其用于一般用途,但它可能在链接问题中提到的狭义情况下很有用,其中海报想要一种方法来为触发器和函数提供应用程序级用户名。
答案 3 :(得分:3)
我发现最好的混合方法是
CREATE TABLE vars (
id INT NOT NULL PRIMARY KEY DEFAULT 1,
zipcode INT NOT NULL DEFAULT 90210,
-- etc..
CHECK (id = 1)
);
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;
generate_var_getter()
,并重新创建不可变的var_get()
函数。CREATE FUNCTION vars_regenerate_update()
RETURNS TRIGGER AS $$
BEGIN
PERFORM generate_var_getter();
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_vars_regenerate_change
AFTER INSERT OR UPDATE ON vars
EXECUTE FUNCTION vars_regenerate_update();
现在,您可以轻松地将变量保存在表中,而且还可以对它们进行快速,不变的访问。两全其美:
INSERT INTO vars DEFAULT VALUES;
-- INSERT 0 1
SELECT var_get('zipcode')::INT;
-- 90210
UPDATE vars SET zipcode = 84111;
-- UPDATE 1
SELECT var_get('zipcode')::INT;
-- 84111
答案 4 :(得分:2)
我找到了这个解决方案:
with vars as (
SELECT * FROM (values(5)) as t(MY_ID)
)
SELECT * FROM users WHERE id = (SELECT MY_ID FROM vars)