【问题标题】:How can I create a column in postgres from values and selections based on other columns?如何根据其他列的值和选择在 postgres 中创建列?
【发布时间】:2012-07-07 00:48:23
【问题描述】:

我想在我的表中创建一个(或两个)新字段,它是其他字段的串联,这看起来相对简单。但是我用来帮助创建以下字段的case 语法或if/when 语法是什么(GPA_TXTnewfield)?

逻辑是:每个GPA应该是#.#,每个新字段应该是:

name & "-" & GPA_TXT & (
    case where GPA_TXT > 3.3
        set newfield = newfield & 'GradeA',
    case where GPA_TXT >2.7 and GPA_TXT < 3.3
        set newfield = newfield & "GradeB",
    etc...
)

例如:

name         major     GPA(num) GPA_TXT   [newfield]
Bob          sci       2        02.0      Bob-sci-GradeC-02.0
Jane         chem      3.1      03.1      Jane-chem-GradeB-03.1
Charlie      phys      3.7      03.7      Charlie-phys-GradeA-03.7
Garfield     food      0        00.0      Garfield-food-GradeF-00.0

所以我想我在这里有两个问题:

  1. 如何创建 GPA TXT 字段。
  2. 如何编写case语句根据其他字段的值计算一个字段。

如果有人可以将我链接到带有示例或解释的资源,我将不胜感激!我正在查看文档,但没有示例就无法到达任何地方。

【问题讨论】:

  • 条件GPA_TXT &gt; 3.3GPA_TXT &gt; 2.7 and GPA_TXT &lt; 3.3 排除了精确值3.3。这是故意的吗?

标签: postgresql


【解决方案1】:

重要提示:我会根据您当前的表创建一个view,并避免添加新列,因为它们会使您的架构非规范化。阅读更多here

另外,我会为所有标识符使用小写名称以避免引用。

  • 要形成GPA_TXT 字段,您可以使用to_char() 函数:to_char(gpa, 'FM09.0')FM 将避免结果字符串前面的空格);
  • 对于第二个字段,我将使用 GPA 而不是 GPA_TXT 进行数字比较。您可以在the docs 中查看更多CASE 构造,但该块可能是以下块:

    CASE WHEN gpa >= 3.3 THEN 'A'
         WHEN gpa > 2.7 AND gpa < 3.3 THEN 'B'
         WHEN gpa > 0 THEN 'C'
         ELSE 'F' END
    

抱歉,我不知道每个 GPA 的成绩是如何分配的,请相应调整。

视图的结果查询可能是(也在SQL Fiddle):

SELECT name,major,gpa,
       to_char(gpa, 'FM09.0') AS gpa_txt,
       name||'-'||major||'-Grade'||
  CASE WHEN gpa >= 3.3 THEN 'A'
       WHEN gpa > 2.7 AND gpa < 3.3 THEN 'B'
       WHEN gpa > 0 THEN 'C'
       ELSE 'F' END || '-' || to_char(gpa, 'FM09.0') AS adesc
  FROM atab;

要构建视图,只需在此查询之前添加 CREATE VIEW aview AS


编辑

如果您仍然要添加列,以下应该可以解决问题:

ALTER TABLE atab ADD gpa_txt text, ADD adesc text;
UPDATE atab SET
    gpa_txt = to_char(gpa, 'FM09.0'),
    adesc = name||'-'||major||'-Grade'||
      CASE WHEN gpa >= 3.3 THEN 'A'
           WHEN gpa > 2.7 AND gpa < 3.3 THEN 'B'
           WHEN gpa > 0 THEN 'C'
           ELSE 'F' END || '-' || to_char(gpa, 'FM09.0');

【讨论】:

  • 谢谢!一个快速跟进的问题——我需要基于第二步创建表——将新连接的列上的左连接到另一个表。视图仍然适用吗?
  • 当然。只要有视图,它就会被视为所有进一步查询的表。只是这种 table 内部扫描的方式不同:对于普通表,将扫描其内容并返回行,对于视图,将执行其 SQL 并返回行。在这两种情况下,PostgreSQL 都足够聪明,可以选择最适合您的谓词、排序和分组条件的计划。
  • +1 很多好建议。关于 CASE 语句的一个小问题:WHEN gpa &gt; 2.7 AND gpa &lt; 3.3 THEN 'B' 在逻辑上是多余的,可以简化为WHEN gpa &gt; 2.7 THEN 'B'
  • @ErwinBrandstetter,请注意,没有想到。谢谢。不过,为了更好的可见性,我会保持原样。
【解决方案2】:

我建议使用“生成”列,而不是冗余地存储数据。它将占用更少的磁盘空间,这实际上可能比存储生成的值更快,并且肯定不会意外地与基础数据不同步。假设您喜欢@vyegorov 提供的格式,您可以使用表的记录类型(与表名匹配)作为输入来创建这样的函数:

CREATE FUNCTION adesc(rec atab)
  RETURNS text
  IMMUTABLE
  LANGUAGE SQL
AS $$
SELECT to_char($1.gpa, 'FM09.0') AS gpa_txt,
       $1.name||'-'||$1.major||'-Grade'||
  CASE WHEN $1.gpa >= 3.3 THEN 'A'
       WHEN $1.gpa > 2.7 AND $1.gpa < 3.3 THEN 'B'
       WHEN $1.gpa > 0 THEN 'C'
       ELSE 'F' END || '-' || to_char($1.gpa, 'FM09.0') AS adesc;
$$;

您需要使用关系限定符(表名或别名)来引用它。当这样的引用没有通过找到一个实际的列来解决时,PostgreSQL 将寻找一个将表的记录类型作为其唯一参数的函数。所以你可以做这样的事情:

SELECT name, major, gpa, atab.adesc
  FROM atab;

这样的“生成的列”可以在索引中用于快速搜索,如果这是您所追求的,例如adesc(atab).*

【讨论】:

  • @user1397044:这是关于生成列的好建议。您可以在 this related answer 中找到更多解释为什么它有效。
【解决方案3】:

这里是从一个包含 3 列的表中返回值的查询:

select *
, to_char(gpa, '09.9') as gpa_text
, name || '-' || major || '-Grade' ||
case    when gpa between 3.5 and 4.0 then 'A'
    when gpa between 2.5 and 3.4 then 'B'
    when gpa between 1.5 and 2.4 then 'C'
    when gpa between 0.5 and 1.4 then 'D'
    else 'F' end
|| '-' || ltrim(to_char(gpa, '09.9')) as newfield
from students

这是工作代码,这是 Bob 的新字段“Bob-sci-GradeC-02.0”

我强烈建议您不要在数据库中使用文本列来保存数值的副本。我不太清楚为什么我需要 ltrim,格式化的字符串会有一个前导空格似乎很奇怪。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-07
    • 2023-01-12
    • 1970-01-01
    • 2018-09-24
    • 1970-01-01
    • 2021-09-18
    • 1970-01-01
    • 2022-07-06
    相关资源
    最近更新 更多