PostgreSQL参数化查询

时间:2016-05-19 10:28:12

标签: sql postgresql parameters plpgsql reusability

我经常需要提交几乎相同的查询但是使用不同的参数(单个参数但在SELECT …子句中多次(不是WHERE(!)))。

为了说明这一点,它位于duc.opt_level >= 5 /*v_min_level*/位于下方的每个地方:

SELECT duc.id_du, duc.du_type_cd, duc.du_name, duc.du_addr, duc.id_du_def, duc.def_repeat_flg, defc.allows_txt_flg
  ,r.lvl + CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= 5 /*v_min_level*/ AND duc.max_occurs=1 THEN 0 ELSE 1 END ELSE 0 END lvl
  ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= 5 /*v_min_level*/ AND duc.max_occurs=1 THEN r.id_du ELSE duc.id_du END ELSE r.id_du END entid
  ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= 5 /*v_min_level*/ AND duc.max_occurs=1 THEN r.entraw ELSE duc.du_addr END ELSE r.entraw END entraw
  ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= 5 /*v_min_level*/ AND duc.max_occurs=1 THEN r.entnm ELSE duc.du_name END ELSE r.entnm END entnm
  ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= 5 /*v_min_level*/ AND duc.max_occurs=1 THEN r.pentid ELSE r.entid END ELSE r.pentid END pentid
  ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= 5 /*v_min_level*/ AND duc.max_occurs=1 THEN r.pentraw ELSE r.entraw END ELSE r.pentraw END pentraw
  ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= 5 /*v_min_level*/ AND duc.max_occurs=1 THEN r.pentnm ELSE r.entnm END ELSE r.pentnm END pentnm
  ,defc.def_type, defc.val_type
  ,defc.is_nillable
  ,defc.optional
FROM r
JOIN data_unit duc
  ON duc.id_parent_du = r.id_du
JOIN du_def defc
  ON defc.id_du_def = duc.id_du_def

...

Postgres ver。 9.5.1

参数化这个查询的任何优雅方式,所以我只能通过重新定义这个参数来调用它吗?

出现了一些关于匿名PL / pgSQL块的内容,但我不确切知道如何

2 个答案:

答案 0 :(得分:0)

您可以交叉加入"值"表:

SELECT duc.id_du, duc.du_type_cd, duc.du_name, duc.du_addr, duc.id_du_def, duc.def_repeat_flg, defc.allows_txt_flg
      ,r.lvl +  
         CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= p.min_level AND duc.max_occurs=1 THEN 0 ELSE 1 END ELSE 0 END lvl
      ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= p.min_level AND duc.max_occurs=1 THEN r.id_du ELSE duc.id_du END ELSE r.id_du END entid
      ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= p.min_level AND duc.max_occurs=1 THEN r.entraw ELSE duc.du_addr END ELSE r.entraw END entraw
      ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= p.min_level AND duc.max_occurs=1 THEN r.entnm ELSE duc.du_name END ELSE r.entnm END entnm
      ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= p.min_level AND duc.max_occurs=1 THEN r.pentid ELSE r.entid END ELSE r.pentid END pentid
      ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= p.min_level AND duc.max_occurs=1 THEN r.pentraw ELSE r.entraw END ELSE r.pentraw END pentraw
      ,CASE duc.du_type_cd WHEN 'e' THEN CASE WHEN duc.opt_level >= p.min_level AND duc.max_occurs=1 THEN r.pentnm ELSE r.entnm END ELSE r.pentnm END pentnm
      ,defc.def_type, defc.val_type
      ,defc.is_nillable
      ,defc.optional
FROM ...
  cross join (values (5)) as p(min_level)

答案 1 :(得分:0)

假设 所有列都不能为NULL(问题中缺少信息)。

CASE表达式解释为:

后,您的任务变得更加简单
 , CASE WHEN duc.du_type_cd = 'e' AND (duc.opt_level < 5 OR duc.max_occurs <> 1) THEN r.lvl + 1   ELSE r.lvl     END AS lvl
 , CASE WHEN duc.du_type_cd = 'e' AND (duc.opt_level < 5 OR duc.max_occurs <> 1) THEN duc.id_du   ELSE r.id_du   END AS entid
 , CASE WHEN duc.du_type_cd = 'e' AND (duc.opt_level < 5 OR duc.max_occurs <> 1) THEN duc.du_addr ELSE r.entraw  END AS entraw
  -- etc.

相同的重复条件可以集中在LEFT JOIN列表中的rr1}的另一个FROM个实例中。如果不满足条件,则默认使用COALESCE的替代值:

SELECT duc.id_du, duc.du_type_cd, duc.du_name, duc.du_addr, duc.id_du_def, duc.def_repeat_flg
     , defc.allows_txt_flg, defc.def_type, defc.val_type, defc.is_nillable, defc.optional
     , COALESCE(r1.lvl    , r.lvl + 1)   AS lvl
     , COALESCE(r1.id_du  , duc.id_du)   AS entid
     , COALESCE(r1.entraw , duc.du_addr) AS entraw
     , COALESCE(r1.entnm  , duc.du_name) AS entnm
     , COALESCE(r1.pentid , r.entid)     AS pentid
     , COALESCE(r1.pentraw, r.entraw)    AS pentraw
     , COALESCE(r1.pentnm , r.entnm)     AS pentnm
FROM   duc
JOIN   defc ON ???  -- missing information
JOIN   r    ON ???  -- missing information
LEFT   JOIN r1 ON duc.du_type_cd <> 'e'
               OR (duc.opt_level >= 5 /*v_min_level*/ AND duc.max_occurs = 1);

现在您只提供 v_min_level 一次。查询要短得多。也可能会快一点。

永远不要省略列别名的关键字ASThe manual:

  

省略AS关键词
  ...
  在FROM项中,标准和PostgreSQL都允许省略AS   在作为未保留关键字的别名之前。但这是不切实际的   输出列名称,因为语法含糊不清。