我正在将数据库从 SQL Server 迁移到 Postgresql,目前我正致力于转换此查询。我参考了文档,但似乎没有找到任何解决方案。
这是查询(未声明的
@variables
是 .NET 变量):
declare @table_name as varchar(64)
set @table_name = (select tbl_name from tbl1 where id = @id)
if @table_name = 'xyz'
begin
exec('select col1, col2, col3, col4 from '+@table_name+' where condition1='+@vbl1+' and condition2='+@vbl2+' and condition3=''abc'' and condition4='''+@vbl4+'''
union all
select col1, col2, col3, col4 from '+@table_name+' where condition1='+@vbl1+' and isnull(col3,''0'')=''0'' and condition3=''abc'' and condition4='''+@vbl4+'''
')
end
else
begin
exec('select col1, col2, col3, col4 from '+@table_name+' where condition1='+@vbl1+' and condition2='+@vbl2+' and condition3=''abc''
union all
select col1, col2, col3, col4 from '+@table_name+' where condition1='+@vbl1+' and isnull(col3,''0'')=''0'' and condition3=''abc''
')
end
我尝试转换它,这就是我能做的:
create function ufn_function1(V_tbl_name anyelement, V_id integer, vbl1 integer, vbl2 integer, vbl4 integer) returns setof anyelement as $$
declare
V_table_name varchar:=(select V_tbl_name from tbl1 where id=V_id);
begin
if V_table_name= 'xyz'
then
return query execute format('
select col1, col2, col3, col4 from '||V_table_name||' where condition1='||vbl1||' and condition2='||vbl2||' and condition3=''abc'' and condition4='''||vbl4||'''
union all
select col1, col2, col3, col4 from '||V_table_name||' where condition1='||vbl1||' and isnull(col3,''0'')=''0'' and condition3=''abc'' and condition4='''||vbl4||'''
');
else
return query execute format('
select col1, col2, col3, col4 from '||V_table_name||' where condition1='||vbl1||' and condition2='||vbl2||' and condition3=''abc''
union all
select col1, col2, col3, col4 from '||V_table_name||' where condition1='||vbl1||' and isnull(col3,''0'')=''0'' and condition3=''abc''
');
end if;
end;
$$ language 'plpgsql';
显然这不是正确的转换,并且有多个语法错误,因为我对 Postgresql 完全陌生。
非常感谢任何转换此查询的帮助。
你真的需要重新考虑你的方法。
一般来说,普通 SQL 不允许对标识符(表或列名称等)进行插值/参数化。您需要连接查询字符串,然后执行它。在 PL/pgSQL 中,您可以使用 EXECUTE
执行这种
“动态 SQL”。函数
format()
对于安全连接字符串很有帮助。
但是函数的返回类型不能是完全动态的。解决方法有限。就像返回匿名记录(我觉得这不是很有用)。或者是多态函数。参见:
但这需要您通过调用传递返回类型,这在您的工作流程中是不可能的,您只传递 ID 来获取表名称。另外,您的原始代码似乎效率低下并且对 SQL 注入开放。 然而,你的返回类型看起来毕竟是常数。如果是这样,这可能有效:
CREATE OR REPLACE FUNCTION ufn_function1(_id int, _vbl1 int, _vbl2 int, _vbl4 int)
-- OUT columns must match result!
-- Falling back to text while undisclosed
RETURNS TABLE (col1 text, col2 text, col3 text, col4 text)
LANGUAGE plpgsql AS
$func$
DECLARE
-- the cast to regclass raises an exception immediately
-- if tbl1.tbl_name is not a valid table name (or null)
_tbl regclass := (SELECT tbl_name::regclass FROM tbl1 WHERE id = _id);
BEGIN
IF _table_name IS NULL THEN -- no table name, no go
RAISE WARNING 'No table name found!';
ELSIF _table_name = 'xyz' THEN -- constant table name, plain SQL
RETURN QUERY
-- casting result columns to text while actual types are undisclosed
SELECT t.col1::text, t.col2::text, t.col3::text, t.col4::text
FROM xyz t
WHERE t.condition1 = _vbl1
AND t.condition2 = _vbl2
AND t.condition4 = _vbl4
AND (t.condition3 = 'abc' OR t.col3 <> '0' IS NOT TRUE);
ELSE -- dynamic table name, dynamic SQL
RETURN QUERY EXECUTE format(
$q$
SELECT t.col1::text, t.col2::text, t.col3::text, t.col4::text
FROM %I t
WHERE t.condition1 = $1
AND t.condition2 = $2
AND t.condition4 = $3
AND (t.condition3 = 'abc' OR t.col3 <> '0' IS NOT TRUE)
$q$, _table_name)
USING _vbl1 -- $1
, _vbl2 -- $2
, _vbl4 -- $3
;
END IF;
END;
$func$;
这其中有一些微妙的细节。对于一个问题来说太多了。
从学习 PL/pgSQL开始。
相关: