64

我对我的 PostgreSQL 数据库有一个相当复杂的查询,它通过一系列嵌套子查询跨越 4 个表。然而,尽管外观和设置看起来有点棘手,但最终它将基于两个外部参数的匹配(两个字符串需要与不同表中的字段匹配)返回两列(来自同一个表,如果这有助于这种情况)。我对 PostgreSQL 中的数据库设计相当陌生,所以我知道这个看似神奇的东西叫做 Views 存在,它似乎可以帮助我,但也许不是。

有什么方法可以将我的复杂查询移动到视图中,并以某种方式将我需要匹配的两个值传递给它?这将大大简化我在前端的代码(通过将复杂性转移到数据库结构)。我可以创建一个包含我的静态示例查询的视图,它工作得很好,但是只适用于一对字符串值。我需要能够将它与各种不同的值一起使用。

因此,我的问题是:是否可以将参数传递给其他静态视图并使其变为“动态”?或者也许视图不是处理它的正确方法。如果还有其他更好的方法,我会全力以赴!

*编辑:*根据评论中的要求,这是我现在的查询:

SELECT   param_label, param_graphics_label
  FROM   parameters
 WHERE   param_id IN 
         (SELECT param_id 
            FROM parameter_links
           WHERE region_id = 
                 (SELECT region_id
                    FROM regions
                   WHERE region_label = '%PARAMETER 1%' AND model_id =
                         (SELECT model_id FROM models WHERE model_label = '%PARAMETER 2%')
                 )
         ) AND active = 'TRUE'
ORDER BY param_graphics_label;

参数由上面的百分比符号设置。

4

5 回答 5

80

您可以使用集合返回函数:

create or replace function label_params(parm1 text, parm2 text)
  returns table (param_label text, param_graphics_label text)
as
$body$
  select ...
  WHERE region_label = $1 
     AND model_id = (SELECT model_id FROM models WHERE model_label = $2)
  ....
$body$
language sql;

然后你可以这样做:

select *
from label_params('foo', 'bar')

顺便说一句:你确定你想要:

AND model_id = (SELECT model_id FROM models WHERE model_label = $2)

如果model_label不是唯一的(或主键),那么这最终会引发错误。你可能想要:

AND model_id IN (SELECT model_id FROM models WHERE model_label = $2)
于 2012-07-09T19:43:35.123 回答
31

除了 @a_horse 已经清除的内容之外,您还可以通过使用JOIN 语法而不是嵌套子查询来简化您的 SQL。性能将相似,但语法更短且更易于管理。

CREATE OR REPLACE FUNCTION param_labels(_region_label text, _model_label text)
  RETURNS TABLE (param_label text, param_graphics_label text) AS
$func$
    SELECT p.param_label, p.param_graphics_label
    FROM   parameters      p 
    JOIN   parameter_links l USING (param_id)
    JOIN   regions         r USING (region_id)
    JOIN   models          m USING (model_id)
    WHERE  p.active
    AND    r.region_label = $1 
    AND    m.model_label = $2
    ORDER  BY p.param_graphics_label;
$func$ LANGUAGE sql;
  • 如果model_label不是唯一的或查询中的其他内容产生重复的行,您可能希望SELECT DISTINCT p.param_graphics_label, p.param_label使用匹配ORDER BY子句以获得最佳性能。或者使用GROUP BY从句。

  • $1从 Postgres 9.2 开始,您可以使用声明的参数名称来代替$2SQL 函数。(长期以来,PL/pgSQL 函数都可以使用)。

  • 必须注意避免命名冲突。这就是为什么我习惯在声明中为参数名称添加前缀(这些在函数内部随处可见)并在正文中使用表限定列名称。

  • 我简化WHERE p.active = 'TRUE'WHERE p.active,因为该列 active很可能是类型boolean,而不是text

  • USING仅当列名在 JOIN 左侧的所有表中都明确时才有效。否则,您必须使用更明确的语法:
    ON l.param_id = p.param_id

于 2012-07-09T20:15:38.950 回答
26

在大多数情况下,set-returning 函数是要走的路,但如果您想同时读取和写入 set,视图可能更合适。视图可以读取会话参数

CREATE VIEW widget_sb AS
  SELECT * FROM widget
  WHERE column = cast(current_setting('mydomain.myparam') as int)

SET mydomain.myparam = 0
select * from widget_sb
[results]

SET mydomain.myparam = 1
select * from widget_sb
[distinct results]
于 2014-08-12T04:32:42.037 回答
3

我认为您所说的“动态”视图是不可能的。

为什么不编写一个带有 2 个参数的存储过程呢?

于 2012-07-09T19:40:55.880 回答
1

我将查询改写如下:

SELECT   p.param_label, p.param_graphics_label
  FROM   parameters p
where exists (
    select 1
    from parameter_links pl
    where pl.parameter_id = p.id
    and exists (select 1 from regions r where r.region_id = pl.region_id
) and p.active = 'TRUE'
order by p.param_graphics_label;

假设您在各种 id 列上都有索引,则此查询应该比使用 IN 运算符快得多;这里的exists参数将只使用索引值,甚至不接触数据表,除了从参数表中获取最终数据。

于 2015-08-10T21:29:17.933 回答