用于创建和填充数据的 PostgreSQL 函数在运行后似乎没有做任何事情

PostgreSQL function to create and populate data doesn't seem to do anything after it runs

这是我正在尝试做的事情:

我有一个巨大的 table,有数百万行。我将提供一个更简单的示例:

Table Living_Things:

Type        |Category       |Name       
Bird        |Parrot         |Sophie
Animal      |Dog            |Momo
Animal      |Cat            |Daisy
Animal      |Dog            |Tom
Bird        |Parrot         |Fire
Animal      |Cat            |Moon

我想达到的效果如下:

  1. 创建函数 create_and_insert() 其中:
  2. 获取类别作为输入
  3. 创建一个变量并将“Category_”, , “Table” 的连接结果存储到其中。此外,将可能出现在类别输入中的大多数特殊字符替换为下划线。所以 Dog 会变成 Category_Dog_Table; “高大的树”将变成Category_Tall_Tree_Table.
  4. 使用在步骤 (b) 中创建的名称创建一个 table,并将该类别中的所有内容插入到这个新的 table
  5. 在 table 上创建索引。请注意 table 有数百万行,因此我们不希望在每次插入时都进行索引。所以在最后做这个。

然后我们使用以下方式调用它:

  1. 创建一个执行以下操作的未命名块:
  2. 从 table Living_Things
  3. 中选择不同的类别
  4. 为每个不同的类别调用函数 create_and_insert()

所以在这个过程结束时,我们将有以下新的 tables:

1.  Category_Parrot_Table:
Bird        |Parrot     |Sophie
Bird        |Parrot     |Fire

2.  Category_Dog_Table:
Animal      |Dog            |Momo
Animal      |Dog            |Tom

3.  Category_Cat_Table:
Animal      |Cat            |Daisy
Animal      |Cat            |Moon

以下是我尝试实现此目的的尝试,但它似乎没有做任何事情:

  1. 创建函数:
    DROP FUNCTION IF EXISTS public.create_and_insert (text) ;
    CREATE OR REPLACE FUNCTION public. create_and_insert(
       cat_name text)
        RETURNS void
        LANGUAGE 'plpgsql'
    AS $BODY$
    DECLARE
        cat_table_name text;
    BEGIN
        SELECT CONCAT('Category_', replace(replace(trim(BOTH ' "' FROM regexp_split_to_table(trim(BOTH '[]' FROM cat_name), E'\,')),':','_'),'-','_'), '_Table')
        INTO cat_table_name;
    
        -- EXECUTE format(
        PERFORM format(
            'CREATE TABLE %I AS
            SELECT * FROM Living_Things
            WHERE Category = %I', cat_table_name, cat_name
    
        );
        EXECUTE 'CREATE INDEX ' || cat_table_name ||'_idx ON ' || cat_table_name ||' USING spgist ( name )';
    END;
    $BODY$;
    
    completed in 12 ms
  1. 调用每个类别的函数:

    DO
    $$
    DECLARE cat_name text;
    BEGIN
    FOR cat_name IN (SELECT DISTINCT Category FROM Living_Things) LOOP
        PERFORM public. create_and_insert(cat_name);
        -- EXECUTE public. create_and_insert( cat_name );
        END LOOP;
    END;
    $$;
    completed in 45 s 633 ms

但是创建了预期的 table 中的 None。我做错了什么?

注1:我知道这不是最好的归一化,等等。请记住,这是我为了解释情况而创建的假设示例,而不是真实案例。在真实案例中,我正在处理数百万行的制图信息。这也是使用 spgist 进行索引的原因。

注意 2:我使用的是 PostgreSQL 13.3(托管在 AWS RDS 上)

注意 3:以下工作并创建 table,但我希望通过在创建时插入数据来优化它。目前,我们依次检查 Living_Things table 并将其插入使用以下步骤一一创建的 100 个 table 之一:

    DROP FUNCTION IF EXISTS public.create_cat_table(text) ;
    CREATE OR REPLACE FUNCTION public.create_cat_table(
        cat_table text)
        RETURNS void
        LANGUAGE 'plpgsql'
    AS $BODY$
    BEGIN
        EXECUTE format(
            'CREATE TABLE %I (
                Type varchar,
                Category varchar,
                Name varchar,
                PRIMARY KEY ( Name )
             )', cat_table);
        EXECUTE 'CREATE INDEX ' || cat_table ||'_idx ON ' || cat_table ||' USING spgist ( name )';
    END;
    $BODY$;

PERFORM format() 是废话。它准备查询(按函数格式)并抛出它。您需要使用 EXECUTE 语句。

我一直在玩 PERFORM 和 EXECUTE,大约在我发布这个问题前 3 天。

当我将查询更改为使用 EXECUTE 和将其更改为使用 PERFORM 时,我不断收到不同的错误。是的,EXECUTE 起作用了,但只有当我解决了真正的问题时。

真正的问题: 如果您看到我在问题中发布的原始代码,我在 create_and_insert() 中使用“WHERE Category = %I”。 %I 是问题,因为它试图在其中插入 cat_name 的值作为标识符。但在这种情况下,用法与字面意思相同。所以将 %I 更改为 %L 是这里缺少的块

注意:“RAISE NOTICE”命令是一个非常有用的实用程序,可用于调试 PostgreSQL 中的函数。它通过在调试时打印值帮助我确定“SELECT INTO”正在工作,并且能够帮助我查明它在

中的哪一行代码有问题
DROP FUNCTION IF EXISTS public.create_and_insert(text) ;
CREATE OR REPLACE FUNCTION public.create_and_insert(
    cat_name text)
    RETURNS void
    LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
    cat_table_name text;
BEGIN
    --RAISE NOTICE 'Before select concat - 01';
    SELECT CONCAT('Category_', replace(replace(trim(BOTH ' "' FROM regexp_split_to_table(trim(BOTH '[]' FROM cat_name), E'\,')),':','_'),'-','_'), '_Table')
    INTO cat_table_name;

    --RAISE NOTICE 'After SELECT CONCAT - 02 - %, %', cat_name, cat_table_name;
    EXECUTE format('DROP TABLE IF EXISTS %I', cat_table_name);
    EXECUTE format(
        'CREATE TABLE %I AS
        SELECT * FROM Living_Things
        WHERE nsubtype = %L', cat_table_name, cat_name
    );

    --RAISE NOTICE 'after CREATE TABLE - 03';
    EXECUTE 'CREATE INDEX ' || cat_table_name ||'_idx ON ' || cat_table_name ||' USING spgist (name)';
    --RAISE NOTICE 'after CREATE INDEX - 04';
    EXECUTE 'ALTER TABLE ' || cat_table_name || ' ADD CONSTRAINT ' || cat_table_name || '_pk PRIMARY KEY (some_column);';
    --RAISE NOTICE 'after ADD PRIMARY KEY - 05';
END;
$BODY$;
    

使用这个调用函数:

DO
$$
DECLARE cat_name text;
BEGIN
FOR cat_name IN (SELECT DISTINCT nsubtype as cat_name FROM Living_Things) LOOP
    --RAISE NOTICE 'Calling create_and_insert( % )', cat_name;
    EXECUTE public.create_and_insert( cat_name );
    END LOOP;
END;
$$;