【问题标题】:Delete row if type cast fails如果类型转换失败,则删除行
【发布时间】:2014-06-09 00:58:36
【问题描述】:

好的,这是布局:
我有一堆uuid 格式的varchar 数据。我知道uuid 是它自己的类型。这就是我获取数据的方式。因此,为了验证哪些是uuid,我将varchar 类型中的uuid 插入到列为uuid 的表中。如果插入失败,则它不是uuid 类型。我的基本问题是如果插入失败,如何删除坏的uuid。或者,如果在另一个表中插入失败,我如何从一个表中删除。
我的第一组数据:

drop table if exists temp1;
drop sequence if exists temp1_id_seq;
CREATE temp table temp1 (id serial, some_value varchar);
INSERT INTO temp1(some_value) 
SELECT split_part(name,':',2) FROM branding_resource WHERE name LIKE '%curric%';
create temp table temp2 (id serial, other_value uuid);

CREATE OR REPLACE function verify_uuid() returns varchar AS $$
DECLARE uu RECORD;
BEGIN
FOR uu IN  select * from temp1 
LOOP
    EXECUTE 'INSERT INTO temp2 values ('||uu.id||','''|| uu.some_value||''')';
END LOOP;
END;
$$
LANGUAGE 'plpgsql' ;
select verify_uuid();

当我运行它时,我得到了错误

错误:uuid 的输入语法无效:

这是我所期望的。我的数据集中有一些不好的 uuid。

我的研究使我找到了文档中的Trapping Errors - Exceptions with UPDATE/INSERT
缩小到重要部分:

BEGIN
FOR uu IN  select * from temp1 
LOOP
    begin
    EXECUTE 'INSERT INTO temp2 values ('||uu.id||','''|| uu.some_value||''')';
    return;
    exception when ??? then delete from temp1 where some_value = uu.some_value;
    end;
END LOOP;
END;

我不知道该放什么来代替???。我认为它与ERROR: invalid input syntax for uuid: 有关,但我不确定。实际上我什至不确定这是否是正确的方法?

【问题讨论】:

  • 您的 Postgres 版本是必不可少的。

标签: sql postgresql exception-handling plpgsql uuid


【解决方案1】:

您可以使用VERBOSE模式从psql获取SQLSTATE代码,例如:

regress=> \set VERBOSITY verbose
regress=> SELECT 'fred'::uuid;
ERROR:  22P02: invalid input syntax for uuid: "fred"
LINE 1: SELECT 'fred'::uuid;
               ^
LOCATION:  string_to_uuid, uuid.c:129

在这里我们可以看到SQLSTATE22P02。您可以直接在异常子句中使用它,但通常look it up in the manual 更易于查找文本表示。在这里,我们看到22P02invalid_text_representation

所以你可以写exception when invalid_text_representation then ...

【讨论】:

    【解决方案2】:

    @Craig 显示了一种识别SQLSTATE 的方法。
    您还可以使用 pgAdmin,它默认显示 SQLSTATE

    SELECT some_value::uuid FROM temp1
    
    > ERROR: invalid input syntax for uuid: "-a0eebc999c0b4ef8bb6d6bb9bd380a11"
    > SQL state: 22P02
    

    我要解决更大的问题:

    实际上我什至不确定这是否是正确的方法?

    您的基本方法是正确的方法:'parking in new york' method (quoting Merlin Moncure in this thread on pgsql-general)。但是这个过程是不必要的昂贵。可能要快得多:

    立即排除明显违规的字符串。

    您应该能够通过更便宜的正则表达式测试来清除大部分违规字符串。
    Postgres accepts a couple of different formats for UUID in text representation,但据我所知,这个字符类应该涵盖所有 有效字符:

    '[^A-Fa-f0-9{}-]'
    

    您可能可以针对您的特定品牌的 UUID 表示进一步缩小范围(仅小写?没有花括号?没有连字符?)。

    CREATE TEMP TABLE temp1 (id serial, some_value text);
    
    INSERT INTO temp1 (some_value) 
    SELECT split_part(name,':',2)
    FROM   branding_resource
    WHERE  name LIKE '%curric%'
    AND    split_part(name,':',2) !~ '[^A-Fa-f0-9{}-]';

    “不包含非法字符。”

    投射以测试其余部分

    与其填充另一个表,不如只删除(现在为数不多的!)违规行:

    CREATE OR REPLACE function f_kill_bad_uuid()
      RETURNS void AS
    $func$
    DECLARE
       rec record;
    BEGIN
    FOR rec IN
       SELECT * FROM temp1 
    LOOP
       BEGIN
       PERFORM rec.some_value::uuid;             -- no dynamic SQL needed
                                                 -- do not RETURN! Keep looping.
       RAISE NOTICE 'Good: %', rec.some_value;   -- only for demo
       EXCEPTION WHEN invalid_text_representation THEN
          RAISE NOTICE 'Bad: %', rec.some_value; -- only for demo
          DELETE FROM temp1 WHERE some_value = rec.some_value;
       END;
    END LOOP;
    END
    $func$ LANGUAGE plpgsql;
    
    • 不需要动态 SQL。只需投射。使用PERFORM,因为我们对结果不感兴趣。我们只是想看看演员阵容是否通过。

    • 不返回值。您可以计算并返回排除的行数...

    • 对于一次性操作,您还可以使用DO 语句。

    • 并且不要引用语言名称<strike>'plpgsql'</strike>。它是一个标识符,而不是一个字符串。

    SQL Fiddle.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-03
      • 1970-01-01
      • 2020-04-21
      • 1970-01-01
      • 1970-01-01
      • 2019-09-30
      • 1970-01-01
      相关资源
      最近更新 更多