【问题标题】:Alter table add column default and execute the default for each row更改表添加列默认值并为每一行执行默认值
【发布时间】:2014-05-07 18:48:44
【问题描述】:

我正在制作一个函数,它将一个 id 列添加到给定的表中,创建一个序列并填充新的列值。问题是该列已创建,但现在我需要用创建的序列(1、2、3、4、5 ...)的nextval() 填充它。我不知道如何在添加列语句中指定。

CREATE OR REPLACE FUNCTION create_id(tabla character varying)
  RETURNS void AS
$BODY$ 
DECLARE

BEGIN

IF NOT EXISTS (SELECT information_schema.columns.column_name FROM information_schema.columns WHERE information_schema.columns.table_name=tabla AND information_schema.columns.column_name='id')
THEN
    EXECUTE 'ALTER TABLE '|| tabla ||' ADD COLUMN id numeric(8,0)';

    IF NOT EXISTS (SELECT relname FROM pg_class WHERE relname='seq_id_'||tabla) 
    THEN
        EXECUTE 'CREATE SEQUENCE seq_id_'||tabla||' INCREMENT 1  MINVALUE 1  MAXVALUE 9223372036854775807  START 1 CACHE 1';
        EXECUTE 'GRANT ALL ON TABLE seq_id_'||tabla||' TO postgres';
        EXECUTE 'ALTER TABLE ONLY '||tabla||' ALTER COLUMN id SET DEFAULT nextval(''seq_id_'||tabla||'''::regclass)';
    END IF; 
END IF;

RETURN;
END;
$BODY$
  LANGUAGE plpgsql;

【问题讨论】:

  • 您需要更新新的id 列,用nextval 填充?按什么标准排序?
  • 无条件排序,第一个记录第一个值,第二个记录第二个值,第三个记录第三个值..
  • 如果没有排序标准,则没有“第一”和“第二”字...无序选择可能会以它更喜欢的任何顺序返回值。
  • 如果您根本不关心订单,只需添加execute 'update ' || tabla || ' set id = nextval(''seq_id_'||tabla||'')';
  • 是的,谢谢,成功了

标签: postgresql auto-increment plpgsql alter-table column-defaults


【解决方案1】:

您的函数存在许多系列问题。改用这个:

CREATE OR REPLACE FUNCTION f_create_id(_tbl text)
  RETURNS void AS
$func$ 
DECLARE
   _seq text := _tbl || '_id_seq';
BEGIN

IF EXISTS (
   SELECT 1 FROM pg_namespace n
   JOIN   pg_class     c ON c.relnamespace = n.oid
   JOIN   pg_attribute a ON a.attrelid = c.oid 
   WHERE  n.nspname = current_schema()  -- default to current schema
   AND    c.relname = _tbl
   AND    a.attname = 'id'
   AND    NOT a.attisdropped)
THEN
   RAISE EXCEPTION 'Column already exists!'; RETURN;
END IF;

IF EXISTS (
   SELECT 1 FROM pg_namespace n
   JOIN   pg_class     c ON c.relnamespace = n.oid
   WHERE  n.nspname = current_schema()  -- default to current schema
   AND    c.relname = _seq)
THEN
   RAISE EXCEPTION 'Sequence already exists!'; RETURN;
END IF; 

EXECUTE format('CREATE SEQUENCE %I.%I', current_schema(), _seq;    
EXECUTE format($$ALTER TABLE %I.%I ADD COLUMN id numeric(8,0)
               DEFAULT nextval('%I'::regclass)$$  -- one statement!
               , current_schema(), _tbl, _seq);

END
$func$  LANGUAGE plpgsql;

要点

  • 如果您在同一 ALTER TABLE 语句中设置列默认值,则会自动插入值。请注意,这对大表的性能有很大影响,因为必须更新每一行,而添加 NULL 列只需要对系统目录进行微小的更改。

  • 您必须定义 schema 以在其中创建对象。如果您想默认为 current 架构,您仍然必须在查询中考虑这一点目录(或信息模式)表。表名仅与架构名称结合使用是唯一的。
    我使用session information functions current_schema() 找出当前架构。

  • 在使用带有用户输入的动态 SQL 时,您必须防范 SQL 注入。详情:
    Table name as a PostgreSQL function parameter

  • 如果序列已经存在,不要使用它!您可能会干扰现有对象。

  • 通常,您确实不需要需要EXECUTE GRANT ALL ON TABLE ... TO postgres。如果postgres 是超级用户(默认),则该角色拥有所有权限。您可能想让postgres成为所有者。那会有所作为。

  • 我在两个查询中都使用系统目录,而您在其中一个查询中使用信息架构。我一般不喜欢信息模式。它臃肿的视图是。提供的信息符合跨数据库标准,但是在编写 plpgsql 函数时有什么好处,无论如何这些函数都是 100% 不可移植的?

优越的替代品

  • 我建议不要使用列名id,这是一种 SQL 反模式。请改用正确的描述性名称,例如tablename || '_id'

  • 使用numeric(8,0) 有什么意义?如果您不想要小数位数,为什么不使用integer?更简单、更小、更快。

鉴于此,您最好使用serial type,让一切变得更简单:

CREATE OR REPLACE FUNCTION f_create_id(_tbl text)
  RETURNS void AS
$func$ 
BEGIN

IF EXISTS (
   SELECT 1 FROM pg_namespace n
   JOIN   pg_class     c ON c.relnamespace = n.oid
   JOIN   pg_attribute a ON a.attrelid = c.oid 
   WHERE  n.nspname = current_schema()  -- default to current schema
   AND    c.relname = _tbl
   AND    a.attname = _tbl || '_id'     -- proper column name
   AND    NOT a.attisdropped)
THEN
   RAISE EXCEPTION 'Column already exists!';
ELSE
   EXECUTE format('ALTER TABLE %I.%I ADD COLUMN %I serial'
                 , current_schema(), _tbl, _tbl || '_id');
END IF;

END
$func$  LANGUAGE plpgsql;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-06
    • 1970-01-01
    • 2012-07-03
    • 2019-03-16
    • 1970-01-01
    • 2023-03-31
    • 2019-07-05
    相关资源
    最近更新 更多