用于创建和填充数据的 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
我想达到的效果如下:
- 创建函数 create_and_insert() 其中:
- 获取类别作为输入
- 创建一个变量并将“Category_”, , “Table” 的连接结果存储到其中。此外,将可能出现在类别输入中的大多数特殊字符替换为下划线。所以 Dog 会变成 Category_Dog_Table; “高大的树”将变成Category_Tall_Tree_Table.
- 使用在步骤 (b) 中创建的名称创建一个 table,并将该类别中的所有内容插入到这个新的 table
- 在 table 上创建索引。请注意 table 有数百万行,因此我们不希望在每次插入时都进行索引。所以在最后做这个。
然后我们使用以下方式调用它:
- 创建一个执行以下操作的未命名块:
- 从 table Living_Things
中选择不同的类别
- 为每个不同的类别调用函数 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
以下是我尝试实现此目的的尝试,但它似乎没有做任何事情:
- 创建函数:
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
- 调用每个类别的函数:
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;
$$;
这是我正在尝试做的事情:
我有一个巨大的 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
我想达到的效果如下:
- 创建函数 create_and_insert() 其中:
- 获取类别作为输入
- 创建一个变量并将“Category_”, , “Table” 的连接结果存储到其中。此外,将可能出现在类别输入中的大多数特殊字符替换为下划线。所以 Dog 会变成 Category_Dog_Table; “高大的树”将变成Category_Tall_Tree_Table.
- 使用在步骤 (b) 中创建的名称创建一个 table,并将该类别中的所有内容插入到这个新的 table
- 在 table 上创建索引。请注意 table 有数百万行,因此我们不希望在每次插入时都进行索引。所以在最后做这个。
然后我们使用以下方式调用它:
- 创建一个执行以下操作的未命名块:
- 从 table Living_Things 中选择不同的类别
- 为每个不同的类别调用函数 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
以下是我尝试实现此目的的尝试,但它似乎没有做任何事情:
- 创建函数:
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
- 调用每个类别的函数:
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;
$$;