PL / pgSQL用于一体化动态查询

时间:2018-01-11 22:38:27

标签: sql postgresql database-design plpgsql dynamic-sql

我正在使用PostigreSQL10。我有一个多合一查询,用户可以选择类别样式,事件,区域的非标准组合来搜索构造。请记住,类别样式,事件,区域位于不同的表中。

我想避免多个IFJOIN。我还想避免查询规划器缓存错误的参数组合的错误计划和每次查询的续集重新编译。所以我必须使用动态SQL。

要在PostgreSQL中获取动态SQL,我必须使用PL / pgSQL。但是,根据其文件

  

在PL / pgSQL函数中生成动态命令,即   涉及不同表或不同数据类型的命令   每次他们被处决。 PL / pgSQL正常尝试缓存计划   for命令在这种情况下不起作用。处理这种情况   问题,提供了EXECUTE语句。此外,没有计划   缓存通过EXECUTE执行的命令。相反,命令是   每次运行语句时始终计划。如果是多行   返回时,只有第一个将被分配给INTO变量 - here

  

直接出现在PL / pgSQL函数中的SQL命令必须引用   每次执行都有相同的表和列;也就是说,你做不到   使用参数作为SQL命令中表或列的名称。至   绕过这个限制,你可以使用构造动态命令   PL / pgSQL EXECUTE语句 - 以执行新解析的代价   分析并构建每次执行的新执行计划 - here

所以,我猜PL / pgSQL不适合我的情况,因为我有多个表。

我的问题是:PL / pgSQL实际上不适合我的情况,还是我错过了什么? 子问题:如果不合适,我怎么能为postgreSQL语法动态sql,我找不到任何教程。

由于

2 个答案:

答案 0 :(得分:1)

您能否发布一些表格定义以及您尝试做的样本查询?我并不是百分之百地确定你所追求的是什么,但有几种形式的"动态"使用存储过程/函数的SQL:

  1. 创建一个接受输入参数的函数(即categoryType,styleId,eventName,areaId)并将这些值插入"静态" SQL请求。这是针对您的案例的示例查询代码段:
  2. SELECT *
    FROM category cat
    INNER JOIN style st ON cat.styleid = style.id
    WHERE (cat.categoryType = pCategoryType OR pCategoryType IS NULL)
    AND (st.id = pStyleId OR pStyleId IS NULL)
    

    这是一个真实的例子:

    CREATE OR REPLACE FUNCTION SP_IGLGetItem(
        pItemId INTEGER
    ) 
    RETURNS TABLE(
        ItemId INTEGER,
        ItemName VARCHAR(100),
        ItemCategory CHAR(2) 
    AS
    $$
    BEGIN
        RETURN QUERY
        SELECT i.ItemId, i.ItemName, i.ItemCategory
        FROM Item i
        WHERE (i.ItemId = pItemId OR pItemId IS NULL) -- Return single item (if specified, otherwise return all)
        ;
    END;
    $$
    LANGUAGE 'plpgsql';
    
    1. 根据不同的条件,参数值等构建一个包含要动态执行的SQL的字符串。这是动态的。

    2. 有条件地运行不同的"静态"基于输入参数值的SQL语句。

    3. 这些是否与您的情况相符?

      PL / PGSQL只是用于在Postgres中编写存储过程/函数的语言。如果你确实需要动态SQL生成,那么你最好的选择是使用PL / PGSQL编写一个函数。

      另一种选择是在客户端应用程序中动态生成所需的SQL,然后直接提交该SQL以便执行。

答案 1 :(得分:1)

您可以在pl / pgsql execute。

中运行大多数查询

例如,这2个表连接选择将正常工作:

drop table if exists dyn_tab1;
create table dyn_tab1 (id int primary key, value text);
insert into dyn_tab1 values (1, 'test1'), (2, 'test2');

drop table if exists dyn_tab2;
create table dyn_tab2
  (id serial primary key, fk_id int references dyn_tab1(id), value text);
insert into dyn_tab2 (fk_id, value)
values (1, 'blahblah'), (1, 'blahblah3'), (1, 'foobar'), (2, 'asdf');

select *
from dyn_tab1 as t1
join dyn_tab2 as t2 on t2.fk_id = t1.id;

--I'm mixing here both format and USING (prepared statement).
--You can use format for everything tho. Or just concat strings.
do $$
declare
  l_row record;
begin
for l_row in 
execute format($query$ select *
                       from %I as t1
                       join %I as t2 on t2.fk_id = t1.id
                       where t1.id = $1;
               $query$, 'dyn_tab1', 'dyn_tab2')
using 2
loop
raise notice 'record: %', l_row;
end loop;
end;
$$;

create function dyn_test()
returns setof record
as $$
begin
return query execute format($query$ select *
                       from %I as t1
                       join %I as t2 on t2.fk_id = t1.id
                       where t1.id = $1;
               $query$, 'dyn_tab1', 'dyn_tab2')
using 2;
end;
$$ language plpgsql;

select * from dyn_test() as (id int, value text, id2 int, fk int, value2 text);