关于postgresql:如何根据其他列的值和选择在postgres中创建一个列?

How can I create a column in postgres from values and selections based on other columns?

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

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

1
2
3
4
5
6
7
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...
)

例如:

1
2
3
4
5
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

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

  • 如何创建GPA TXT字段。
  • 如何根据其他字段中的值编写case语句来计算字段。
  • 如果有人可以通过示例或解释将我链接到资源,我会非常感激! 我正在查看文档,但没有示例,没有任何地方。


    重要说明:我将根据您当前的表创建一个视图,并避免添加新列,因为它们会对您的模式进行非规范化。在这里阅读更多。

    此外,我将使用小写名称为所有标识符,以避免qouting。

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

      1
      2
      3
      4
      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上):

    1
    2
    3
    4
    5
    6
    7
    8
    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

    编辑

    如果您仍然要添加列,以下应该可以做到:

    1
    2
    3
    4
    5
    6
    7
    8
    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');


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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将查找将表的记录类型作为其唯一参数的函数。所以你可以做这样的事情:

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

    这样一个"生成列"可以用于快速搜索的索引中,如果你正在使用的话,可以使用adesc(atab).*之类的东西。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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,格式化的字符串会有一个前导空白似乎很奇怪。