当我有一个很大的结果时,Postgres 函数不工作
Postgres Function not working when I have a large result
我正在将信息从 table 1(tmp_subtype) 复制到 table 2(subtype_user)。我有一个带有 15 个寄存器的测试 table 1。我运行把这个函数改成postgres:
CREATE OR REPLACE FUNCTION VERIFY_AND_INSERT_SUPTYPE()
RETURNS text AS $$
DECLARE
register_subtype RECORD;
existe INT DEFAULT 0;
MESSAGE_EXCEPTION TEXT;
cursor_subtype CURSOR
FOR
SELECT tsd.subtype,tsd.type_id_client,tsd.id_client,tsd.email
FROM tmp_subtype tsd;
BEGIN
OPEN cursor_subtype;
LOOP
FETCH cursor_subtype INTO register_subtype;
EXIT WHEN NOT FOUND;
SELECT COUNT(*) INTO existe FROM (
SELECT sdu.id_client FROM subtype_user sdu
WHERE sdu.id_client = register_subtype.id_client AND sdu.type_id_client = register_subtype.type_id_client
LIMIT 1
) SUB0;
IF existe = 0 THEN
INSERT INTO subtype_user(subtype,type_id_client,id_client,email)
VALUES (register_subtype.subtype,register_subtype.type_id_client,register_subtype.id_client,register_subtype.email);
ELSE
UPDATE subtype_user sdu2 SET subtype=register_subtype.subtype,email=register_subtype.email
WHERE sdu2.id_client = register_subtype.id_client AND sdu2.type_id_client = register_subtype.type_id_client;
END IF;
END LOOP;
CLOSE cursor_subtype;
RETURN 'OK';
EXCEPTION WHEN OTHERS THEN
GET STACKED DIAGNOSTICS MESSAGE_EXCEPTION = MESSAGE_TEXT;
RETURN MESSAGE_EXCEPTION;
END; $$
LANGUAGE plpgsql;
可以用,但是当我运行这个函数用真正的table1时,就不行了。该功能完成但没有任何反应。真正的table1有100万个寄存器。
很难说,这段代码有什么问题 - 在这种情况下 RAISE NOTICE
是你最好的朋友。我在您的代码中发现了一些问题,但这些问题与性能有关。 Table 100 万行不算什么。
ISAM 编程风格的代码可能真的很慢 - 而不是循环游标使用 INSERT ON CONFLICT ..
statement.
SELECT COUNT(*) ...
可以改写得更快一点,但肯定更具可读性:
IF EXISTS(SELECT ... FROM subtype_user) THEN
UPDATE ...
ELSE
INSERT ...
END IF;
处理您的示例中的错误有点过时 - 只捕获您可以真正解决的异常。您的异常处理类型无法解决任何问题,而且您会丢失有关异常的详细信息(位置、行、...)。只是不要这样做。
带有嵌入式计数的逐行处理会导致处理缓慢且效率低下。此外,如果从并发事务调用该函数,则您的存在性检查将不起作用。据我所知,您可以用单个 INSERT 语句替换整个循环和游标:
CREATE OR REPLACE FUNCTION VERIFY_AND_INSERT_SUPTYPE()
RETURNS text
AS $$
DECLARE
MESSAGE_EXCEPTION TEXT;
BEGIN
INSERT INTO subtype_user(subtype, type_id_client, id_client, email)
SELECT tsd.subtype, tsd.type_id_client, tsd.id_client, tsd.email
FROM tmp_subtype tsd
ON conflict (id_client, type_id_client) DO UPDATE
SET subtype = excluded.register_subtype,
email = excluded.email;
RETURN 'OK';
EXCEPTION WHEN OTHERS THEN
GET STACKED DIAGNOSTICS MESSAGE_EXCEPTION = MESSAGE_TEXT;
RETURN MESSAGE_EXCEPTION;
END; $$
LANGUAGE plpgsql;
我可能不会首先添加异常处理程序,以便调用者看到完整的异常。
我正在将信息从 table 1(tmp_subtype) 复制到 table 2(subtype_user)。我有一个带有 15 个寄存器的测试 table 1。我运行把这个函数改成postgres:
CREATE OR REPLACE FUNCTION VERIFY_AND_INSERT_SUPTYPE()
RETURNS text AS $$
DECLARE
register_subtype RECORD;
existe INT DEFAULT 0;
MESSAGE_EXCEPTION TEXT;
cursor_subtype CURSOR
FOR
SELECT tsd.subtype,tsd.type_id_client,tsd.id_client,tsd.email
FROM tmp_subtype tsd;
BEGIN
OPEN cursor_subtype;
LOOP
FETCH cursor_subtype INTO register_subtype;
EXIT WHEN NOT FOUND;
SELECT COUNT(*) INTO existe FROM (
SELECT sdu.id_client FROM subtype_user sdu
WHERE sdu.id_client = register_subtype.id_client AND sdu.type_id_client = register_subtype.type_id_client
LIMIT 1
) SUB0;
IF existe = 0 THEN
INSERT INTO subtype_user(subtype,type_id_client,id_client,email)
VALUES (register_subtype.subtype,register_subtype.type_id_client,register_subtype.id_client,register_subtype.email);
ELSE
UPDATE subtype_user sdu2 SET subtype=register_subtype.subtype,email=register_subtype.email
WHERE sdu2.id_client = register_subtype.id_client AND sdu2.type_id_client = register_subtype.type_id_client;
END IF;
END LOOP;
CLOSE cursor_subtype;
RETURN 'OK';
EXCEPTION WHEN OTHERS THEN
GET STACKED DIAGNOSTICS MESSAGE_EXCEPTION = MESSAGE_TEXT;
RETURN MESSAGE_EXCEPTION;
END; $$
LANGUAGE plpgsql;
可以用,但是当我运行这个函数用真正的table1时,就不行了。该功能完成但没有任何反应。真正的table1有100万个寄存器。
很难说,这段代码有什么问题 - 在这种情况下 RAISE NOTICE
是你最好的朋友。我在您的代码中发现了一些问题,但这些问题与性能有关。 Table 100 万行不算什么。
ISAM 编程风格的代码可能真的很慢 - 而不是循环游标使用
INSERT ON CONFLICT ..
statement.SELECT COUNT(*) ...
可以改写得更快一点,但肯定更具可读性:IF EXISTS(SELECT ... FROM subtype_user) THEN UPDATE ... ELSE INSERT ... END IF;
处理您的示例中的错误有点过时 - 只捕获您可以真正解决的异常。您的异常处理类型无法解决任何问题,而且您会丢失有关异常的详细信息(位置、行、...)。只是不要这样做。
带有嵌入式计数的逐行处理会导致处理缓慢且效率低下。此外,如果从并发事务调用该函数,则您的存在性检查将不起作用。据我所知,您可以用单个 INSERT 语句替换整个循环和游标:
CREATE OR REPLACE FUNCTION VERIFY_AND_INSERT_SUPTYPE()
RETURNS text
AS $$
DECLARE
MESSAGE_EXCEPTION TEXT;
BEGIN
INSERT INTO subtype_user(subtype, type_id_client, id_client, email)
SELECT tsd.subtype, tsd.type_id_client, tsd.id_client, tsd.email
FROM tmp_subtype tsd
ON conflict (id_client, type_id_client) DO UPDATE
SET subtype = excluded.register_subtype,
email = excluded.email;
RETURN 'OK';
EXCEPTION WHEN OTHERS THEN
GET STACKED DIAGNOSTICS MESSAGE_EXCEPTION = MESSAGE_TEXT;
RETURN MESSAGE_EXCEPTION;
END; $$
LANGUAGE plpgsql;
我可能不会首先添加异常处理程序,以便调用者看到完整的异常。