【问题标题】:Store common query as column?将常见查询存储为列?
【发布时间】:2012-06-25 07:10:03
【问题描述】:

使用 PostgreSQL,我有一些如下所示的查询:

SELECT <col 1>, <col 2>
     , (SELECT sum(<col x>)
        FROM   <otherTable> 
        WHERE  <other table foreignkeyCol>=<this table keycol>) AS <col 3>
FROM   <tbl>

鉴于子选择在每种情况下都是相同的,有没有办法将该子选择存储为表中的伪列?本质上,我希望能够从表 A 中选择一列,该列是表 B 中记录相关的特定列的总和。这可能吗?

【问题讨论】:

  • 您不能为此创建一个视图吗?
  • 显然我可以,是的。谢谢!

标签: postgresql database-design view


【解决方案1】:

有没有办法将该子选择存储为表中的伪列?

建议VIEW like 是一个完全有效的解决方案。去吧。

但还有另一种更适合您的问题的方法。您可以编写一个将表类型作为参数的函数来模拟 “计算字段”“生成的列”强>。

考虑这个来自您的描述的测试用例:

CREATE TABLE tbl_a (a_id int, col1 int, col2 int);
INSERT INTO tbl_a VALUES (1,1,1), (2,2,2), (3,3,3), (4,4,4);

CREATE TABLE tbl_b (b_id int, a_id int, colx int);
INSERT INTO tbl_b VALUES
  (1,1,5),  (2,1,5),  (3,1,1)
, (4,2,8),  (5,2,8),  (6,2,6)
, (7,3,11), (8,3,11), (9,3,11);

创建模拟col3的函数:

CREATE FUNCTION col3(tbl_a)
  RETURNS int8
  LANGUAGE sql STABLE AS
$func$
SELECT sum(colx)
FROM   tbl_b b
WHERE  b.a_id = $1.a_id
$func$;

现在可以查询了:

SELECT a_id, col1, col2, tbl_a.col3
FROM   tbl_a;

甚至:

SELECT *, a.col3 FROM tbl_a a;

注意我是如何写的tbl_a.col3 / a.col3,而不仅仅是col3。这是必不可少的

"virtual column" in Oracle 不同,它自动包含在SELECT * FROM tbl_a 中。您可以为此使用VIEW

为什么会这样?

引用表格列的常用方法是使用属性表示法

从 tbl_a 中选择 tbl_a.col1;

调用函数的常用方法是使用函数表示法

选择 col3(tbl_a);

一般来说,最好坚持这些符合 SQL 标准的规范方式

但 Postgres 也允许属性表示法。这些也有效:

从 tbl_a 中选择 col1(tbl_a);
选择 tbl_a.col3;

More about that in the manual.
你现在可能已经看到了,这是怎么回事。这看起来就像你会添加一个额外的表 tbl_a 列,而 col3() 实际上是一个函数,它将 tbl_a 的当前行(或其别名)作为行类型参数并计算一个价值。

SELECT *, a.col3
FROM   tbl_a AS a;

如果有一个实际的列col3 优先,并且系统不会以行tbl_a 作为参数来查找该名称的函数。

它的“美”:您可以从tbl_a 添加或删除列,最后一个查询将动态返回所有当前列,其中视图只会返回在创建时存在的列(早期绑定与后期绑定) *) 的绑定。
当然,您现在必须先删除依赖函数,然后才能删除表。并且在对表格进行更改时,您必须注意不要使函数无效。

我仍然不会使用它。这对无辜的读者来说太令人惊讶了。

【讨论】:

  • 该函数应该真正标记为STABLE(这意味着如果您在执行单个查询期间多次使用相同的参数调用它,它将返回相同的值),而不是IMMUTABLE (这意味着它将总是为相同的参数返回相同的值,而不管数据库内容或时间的流逝)。这样做的一件事是防止在索引中使用该函数,这是您想要的,否则如果对 tbl_b 进行更改,索引将损坏。如果您只使用作为参数传递的行中的值,IMMUTABLE 就可以了。
  • @kgrittn:当然,我相应地修改了函数声明。忘记涉及另一个表。
  • @ErwinBrandstetter - 但如果你做一个 \d tbl_a;或 SELECT * FROM tbl_a;,您看不到“持久/计算/计算/生成”列?另外,如果您能查看我的答案here,我将不胜感激 - 也许我没有正确使用您的代码?或者这是 PostgreSQL 中的一个错误?
  • @Vérace:该函数不会自动成为表格的一部分,您必须按照说明将其拼写出来。我澄清了一些。
  • @ErwinBrandstetter,我得到一个字段不存在这种字段表示法的错误...stackoverflow.com/questions/56816432/…
【解决方案2】:

除了视图之外,您还可以为求和创建一个函数。

CREATE FUNCTION sum_other_table( key type_of_key ) RETURNS bigint
AS $$ SELECT sum( col_x ) FROM table_1 where table_1.key = key $$ LANGUAGE SQL;

然后将其用作您的聚合器:

SELECT col_1, col_2, sum_other_table( key ) AS col_3
FROM table_2 WHERE table_2.key = key;

请注意,sum_other_table() 的返回类型取决于您要汇总的列的类型。

【讨论】:

    【解决方案3】:

    根据狮子的评论,显然这是通过视图处理的。所以就我而言,我使用了命令:

    CREATE VIEW <viewname> AS
    SELECT *, (SELECT sum(<col x>)
    FROM   <otherTable
    WHERE  <otherTable foreignkeyCol>=<thisTable keycol>) AS <col 3>
    FROM   <tablename>
    

    这实际上给了我另一个包含所需列的表格。

    【讨论】:

      【解决方案4】:

      到目前为止,共有三个答案,所有答案都有效。根据具体情况,其中任何一个都可能是“最佳解决方案”。对于小型表,性能应该非常接近,但它们都不太可能很好地扩展到具有数百万行的表。使用大型数据集获得所需结果的最快方法可能是(使用 Erwin 的设置):

      SELECT a_id, col1, col2, sum(colx)
      FROM tbl_a LEFT JOIN tbl_b b using(a_id)
      GROUP BY a_id, col1, col2;
      

      如果 a_id 被声明为主键,并且在 9.1 或更高版本下运行,则可以简化 GROUP BY 子句,因为 col1col2功能上依赖 a_id.

      SELECT a_id, col1, col2, sum(colx)
      FROM tbl_a LEFT JOIN tbl_b b using(a_id)
      GROUP BY a_id;
      

      视图可以这样定义并且可以扩展,但我不认为对于使用函数的方法会考虑所有相同的执行路径,因此最快的执行路径可能不会使用。

      【讨论】:

      • LANGUAGE SQL 中的简单函数将被内联,查询优化器可能会提出最佳解决方案。在这种特殊情况下是否会发生这种情况是另一个问题。
      • @JohannOskarsson:即使函数是内联的,我的版本也会产生 correlated subquery 而不是 join。我怀疑优化器敢于改变它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-03
      • 2019-08-11
      • 1970-01-01
      • 2013-06-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多