【问题标题】:Combine two columns and add into one new column合并两列并添加到一个新列中
【发布时间】:2012-08-31 22:00:36
【问题描述】:

在 PostgreSQL 中,我想使用 SQL 语句来组合两列并从中创建一个新列。

我正在考虑使用concat(...),但有更好的方法吗?
最好的方法是什么?

【问题讨论】:

    标签: sql postgresql null concatenation


    【解决方案1】:

    总的来说,我同意@kgrittn's advice。去吧。

    但要解决您关于 concat() 的基本问题:如果您需要处理 null 值,新功能 concat() 很有用 - 您的问题和你指的那个。

    如果可以排除空值,那么旧的(SQL 标准)连接运算符|| 仍然是最佳选择,而@luis' answer 就好了:

    SELECT col_a || col_b;
    

    如果您的任一列可以为空,则在这种情况下,结果将为空。你可以用COALESCE防守:

    SELECT COALESCE(col_a, '') || COALESCE(col_b, '');
    

    但是随着参数的增加,这很快就会变得乏味。这就是concat() 的用武之地,它从不 返回null,即使所有 参数都为null。 Per documentation:

    NULL 参数被忽略。

    SELECT concat(col_a, col_b);
    

    两种备选方案的剩余极端情况所有输入列为空在这种情况下,我们仍然会得到一个空字符串'' ,但有人可能想要 null 代替(至少我会)。一种可能的方法:

    SELECT CASE
              WHEN col_a IS NULL THEN col_b
              WHEN col_b IS NULL THEN col_a
              ELSE col_a || col_b
           END;
    

    随着列的增加,这会变得更加复杂。同样,使用concat(),但添加一个特殊条件检查:

    SELECT CASE WHEN (col_a, col_b) IS NULL THEN NULL
                ELSE concat(col_a, col_b) END;
    

    这是如何工作的?
    (col_a, col_b) 是行类型表达式ROW (col_a, col_b) 的简写符号。只有当 all 列为空时,行类型才为空。详细解释:

    另外,使用concat_ws() 在元素之间添加分隔符(ws 表示“带分隔符”)。


    类似于凯文回答中的表达:

    SELECT $1.zipcode || ' - ' || $1.city || ', ' || $1.state;
    

    在 PostgreSQL 8.3(没有concat())中准备空值是很乏味的。一种方式(多种方式):

    SELECT COALESCE(
             CASE
                WHEN $1.zipcode IS NULL THEN $1.city
                WHEN $1.city    IS NULL THEN $1.zipcode
                ELSE $1.zipcode || ' - ' || $1.city
             END, '')
           || COALESCE(', ' || $1.state, '');
    

    函数波动仅为STABLE

    concat()concat_ws()STABLE 函数,而不是 IMMUTABLE,因为它们可以调用取决于区域设置的数据类型输出函数(如 timestamptz_out)。
    Explanation by Tom Lane.

    这禁止它们在索引表达式中直接使用。如果您知道在您的情况下结果实际上是不可变的,您可以使用IMMUTABLE 函数包装器来解决这个问题。此处示例:

    【讨论】:

    • 不错!我没有了解 concat() 函数与 SQL 标准 || 连接运算符的不同 NULL 处理。 (您注意到我在回答中使用NOT NULL 列避开了这个问题。)我现在的工具箱中多了一个工具。
    • 我希望我可以选择多个接受的答案。感谢您的完整解释。感谢所有答案!
    【解决方案2】:

    您无需存储该列即可以这种方式引用它。试试这个:

    设置:

    CREATE TABLE tbl
      (zipcode text NOT NULL, city text NOT NULL, state text NOT NULL);
    INSERT INTO tbl VALUES ('10954', 'Nanuet', 'NY');
    

    我们可以看到我们有“正确的东西”:

    \pset border 2
    SELECT * FROM tbl;
    
    +---------+--------+--------+ |邮政编码 |城市|状态 | +---------+--------+--------+ | 10954 |纳努埃特 |纽约 | +---------+--------+--------+

    现在添加一个带有所需“列名”的函数,该函数将表的记录类型作为其唯一参数:

    CREATE FUNCTION combined(rec tbl)
      RETURNS text
      LANGUAGE SQL
    AS $$
      SELECT $1.zipcode || ' - ' || $1.city || ', ' || $1.state;
    $$;
    

    这将创建一个函数,它可以像表的列一样使用,只要指定表名或别名,如下所示:

    SELECT *, tbl.combined FROM tbl;
    

    显示如下:

    +---------+--------+--------+--------+ |邮政编码 |城市|状态 |结合 | +---------+--------+--------+--------+ | 10954 |纳努埃特 |纽约 | 10954 - 纽约州纳努埃 | +---------+--------+--------+--------+

    这是有效的,因为 PostgreSQL 首先检查一个实际的列,但如果没有找到,并且标识符是用关系名称或别名限定的,它会查找类似上面的函数,并以行作为它的运行它参数,将结果作为列返回。如果您愿意,您甚至可以对这样的“生成的列”进行索引。

    因为您没有在每行中为重复数据使用额外空间,或者在所有插入和更新时触发触发器,所以这通常比其他方法更快。

    【讨论】:

    • "which takes the record type of the table as its only parameter" : 创建表后如何发现?
    • 表的记录类型通常与表同名。请参阅示例——特别是有CREATE TABLE tblCREATE FUNCTION combined(rec tbl)。对于确定的答案,找到表的 pg_class 行的 reltype 列,并使用它来找到匹配的 pg_type 行。
    【解决方案3】:

    你检查字符串连接函数了吗?比如:

    update table_c set column_a = column_b || column_c 
    

    应该可以。更多here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-07-21
      • 1970-01-01
      • 2014-06-26
      • 2020-03-30
      • 1970-01-01
      • 2019-03-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多