Postgresql 相当于 SQL Server 查询

问题描述 投票:0回答:1

我正在将数据库从 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 完全陌生。

非常感谢任何转换此查询的帮助。

postgresql plpgsql dynamic-sql
1个回答
0
投票

你真的需要重新考虑你的方法。

一般来说,普通 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开始。

相关:

© www.soinside.com 2019 - 2024. All rights reserved.