有没有办法在PostgreSQL查询中定义命名常量?例如:
MY_ID = 5;
SELECT * FROM users WHERE id = MY_ID;
Run Code Online (Sandbox Code Playgroud)
Gor*_*off 39
之前已经问过这个问题(如何在PostgreSQL中使用脚本变量?).但是,有时我会使用查询技巧:
with const as (
select 1 as val
)
select . . .
from const cross join
<more tables>
Run Code Online (Sandbox Code Playgroud)
也就是说,我定义了一个名为const的CTE,它具有在那里定义的常量.然后,我可以将其加入到我的查询中,任何级别的任意次数.我发现这在处理日期时非常有用,并且需要处理许多子查询中的日期常量.
Erw*_*ter 38
PostgreSQL没有内置的方法来定义(全局)变量,如MySQL或Oracle.(使用"自定义选项")的解决方法有限.根据您的需要,还有其他方法:
你可以在一个查询的顶部提供值CTE喜欢已经提供@Gordon.
您可以IMMUTABLE
为此创建一个简单的函数:
CREATE FUNCTION public.f_myid()
RETURNS int IMMUTABLE LANGUAGE SQL AS
'SELECT 5';
Run Code Online (Sandbox Code Playgroud)
它必须存在于当前用户可见的模式中,即在各自的模式中search_path
.与模式一样public
,默认情况下.如果安全性存在问题,请确保它是search_path
或者架构中的第一个架构 - 在您的呼叫中限定它:
SELECT public.f_myid();
Run Code Online (Sandbox Code Playgroud)
对数据库中的所有用户(允许访问模式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'
Run Code Online (Sandbox Code Playgroud)
由于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'
Run Code Online (Sandbox Code Playgroud)
它不是一个威胁,因为只有特权用户才能改变全局设置.其他用户只能为自己的会话执行此操作.为了防止这种情况发生在所有的,设置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;
Run Code Online (Sandbox Code Playgroud)
或者改为使用:
... SET search_path = pg_temp, param;
Run Code Online (Sandbox Code Playgroud)
这将允许您(或具有必要权限的任何人)在表中提供全局(持久)默认值param.val
...
请考虑使用SECURITY DEFINER创建功能的手册的相关章节.
但是,这种超级安全的功能不能"内联",并且可能比使用硬连线架构的更简单的替代方案执行得更慢:
CREATE FUNCTION f_val(_id int)
RETURNS text STABLE LANGUAGE SQL AS
'SELECT val FROM pg_temp.val WHERE val_id = $1';
Run Code Online (Sandbox Code Playgroud)
相关答案有更多选择:
我找到了这个解决方案:
with vars as (
SELECT * FROM (values(5)) as t(MY_ID)
)
SELECT * FROM users WHERE id = (SELECT MY_ID FROM vars)
Run Code Online (Sandbox Code Playgroud)
小智 6
我发现混合可用的方法是最好的:
CREATE TABLE vars (
id INT NOT NULL PRIMARY KEY DEFAULT 1,
zipcode INT NOT NULL DEFAULT 90210,
-- etc..
CHECK (id = 1)
);
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
generate_var_getter()
调用,并var_get()
重新创建不可变函数。CREATE FUNCTION vars_regenerate_update()
RETURNS TRIGGER AS $$
BEGIN
PERFORM generate_var_getter();
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
CREATE TRIGGER trigger_vars_regenerate_change
AFTER INSERT OR UPDATE ON vars
EXECUTE FUNCTION vars_regenerate_update();
Run Code Online (Sandbox Code Playgroud)
现在,您可以轻松地将变量保存在表中,而且还可以快速访问它们。两全其美的:
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
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
33889 次 |
最近记录: |