【问题标题】:adding multiple columns via a loop in postgresql通过 postgresql 中的循环添加多列
【发布时间】:2015-02-02 14:41:19
【问题描述】:

我正在尝试在具有多个列的架构中创建一个新表,这些列通过索引命名。

CREATE TABLE rflux (pk SERIAL PRIMARY KEY NOT NULL);

现在我想添加新的列,如 col0 FLOAT、col1、col2、col3、...直到 colN。

我知道我可以做类似的事情

ALTER TABLE rflux add column col0 FLOAT add column col1 FLOAT ... ;

但我不想输入所有内容,因为我需要创建 ~4500 列。我正在尝试使用循环来实现这一点,但我无法让它正常工作。有没有人有任何想法?我已经尝试创建一个函数来执行此操作..

create function addColumns()
returns void
as $$
begin
for i in 1..10 loop
alter table rflux add column tmp float;
alter table rflux rename tmp to col||i;
end loop;
return;
end;
$$
language plpgsql;

然后做 select * from addColumns();

但是在将列重命名为 col||i 时出现错误,甚至只是在我尝试 i 时出现错误。我什至不确定这是否是最好的方法。如何添加多个列,以便使用计数器递增列的名称?

编辑..我知道我不能用 4500 列来做到这一点,但是如果我想用 10 列来解决这个问题,比如说?

谢谢。

【问题讨论】:

  • 4500 列??我认为您可能需要考虑重新设计架构,而不是将糟糕的设计硬塞到数据库中。但是,要执行您想要的操作,您需要在存储过程中使用“动态 SQL”……但您可能不应该这样做。
  • 我真正想要的是以某种方式将 4500 个元素的数组存储到数据库中。我可以将它作为 float[] 的一列存储,或者每个元素都是单独的列。我担心通过 SQLAlchemy 对数组进行访问和切片。据我了解,postgres 将数组存储为字符串元素 {1,2,3}。当 SQLAlchemy 读入它时,它会解析这个字符串并将其转换为元素列表。我将拥有数百万行数据,并且需要过滤数组的某些元素满足给定条件的行子集。所以我认为第二种方式会更好。
  • 如果有一种方法可以将数组存储在单列中,但有一个使用 SQLAlchemy 的快速子集选择机制,该机制适用于数百万行,同时适用于多个同时用户,那么我会全力以赴。我才刚刚开始,所以不知道有什么可能。

标签: postgresql alter-table


【解决方案1】:

您的设计可能更适合数组、hstore 或 json 数据类型。添加 4500 列是一场等待发生的噩梦。

【讨论】:

  • 是的,听起来就是这样,尤其是考虑到 postgresql 中的列限制。您知道这会如何影响在 SQLalchemy 甚至 postgres 中对这些数组的子集进行过滤的效率吗?
  • 如果您使用的是 postgres 9.3,json 非常好,您甚至可以索引 json 对象中的各个数据元素。如果您使用的版本低于 9.3,我会使用 hstore,并且这些子字段也可以被索引。使用数组,您将失去命名字段的能力,并且顺序变得非常重要,所以就个人而言,我会避免这种情况。
  • 嗯。好的。我正在使用 postgres 9.3,所以我也会考虑使用 JSON 和 hstore。与数组相比,我对它们了解不多,但如果它们让我轻松地在查询中选择列中的元素子集,那么我完全赞成。谢谢。
  • JSON数据查询方便:select json_column->>'subfield' from mytable
  • 好吧,我的大部分查询都是通过 SQLalchemy 进行的,所以我必须从那里看看它是如何工作的,但我想它是相似的。
【解决方案2】:

如果有帮助的话:

-- VERSION : POSTGRESQL 9.3

-- FICTIVE TABLE #1

CREATE TABLE table_1 ("YEAR" int, "CODE_SP" text, "TOTAL" int);

INSERT INTO table_1 VALUES
(2035, 'TRUC', 2),
(2035, 'MUCH', 4),
(2036, 'FLIC', 7),
(2036, 'LORD', 2),
(2036, 'TRUC', 8),
(2037, 'MUCH', 2),
(2037, 'FLIC', 2),
(2037, 'FLAC', 5),
(2037, 'MORD', 9),
(2038, 'LOOP', 3),
(2038, 'MUCH', 3);

SELECT * FROM table_1;

-- FICTIVE TABLE #2

CREATE TABLE table_2 ("YEAR" int);

INSERT INTO table_2("YEAR")
SELECT serial
FROM generate_series(2035,2038,1) AS serial;

SELECT * FROM table_2;

-- LOOP FOR ADDING COLUMNS ON TABLE #2 

DO
$do$
    DECLARE colnames TEXT;
BEGIN
FOR colnames IN 
    SELECT "CODE_SP"
    FROM table_1
    GROUP BY "CODE_SP"
    ORDER BY "CODE_SP"
LOOP
    EXECUTE 'ALTER TABLE table_2 ADD COLUMN ' || quote_ident(colnames) || ' INT DEFAULT NULL;'; /* careful: in quoted text, the spaces are important */
END LOOP;
END
$do$;

-- LOOP FOR COMPLETTING CELLS ON TABLE #2 

DO
$do$
    DECLARE id_value TEXT;
BEGIN
FOR id_value IN
    SELECT "CODE_SP"
    FROM table_1
    GROUP BY "CODE_SP"
    ORDER BY "CODE_SP"
LOOP
    EXECUTE 'UPDATE table_2 SET ' || quote_ident(id_value) || ' = table_1."TOTAL" FROM table_1 WHERE table_1."CODE_SP" = ' || quote_literal(id_value) || ' AND table_1."YEAR" = table_2."YEAR";'; /* careful: in quoted text, the spaces are important */
END LOOP;
END
$do$;

【讨论】:

    【解决方案3】:

    我没有找到但自己创建的其他解决方案之一。

    do $$
    declare 
        colnames text[];
        target_array text[]:= array[
                                    ['table1','product_id'],
                                    ['table2','product_id'],
                                    ['table3','product_id']
                                    ];  
        
    begin
       FOREACH colnames SLICE 1 in  ARRAY target_array
       loop
            execute 'alter table '||colnames[1]||' 
                    add  '||colnames[2]||' int
                    DEFAULT NULL;';
            raise notice 'table: %, column: %', colnames[1], colnames[2];
       end loop;
    end; $$;
    

    【讨论】:

      猜你喜欢
      • 2013-05-25
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      • 1970-01-01
      • 1970-01-01
      • 2019-12-26
      • 2016-08-25
      • 1970-01-01
      相关资源
      最近更新 更多