检查行类型时,为什么IS NOT NULL为false?

Len*_*enB 8 postgresql null row plpgsql

我有一个函数registration(),它应该在某些情况下向表添加一行.我已经包含了一段代码和来自通话的输出.

如果select *返回一个非空表行(它根据它执行RAISE NOTICE),我想引发异常而不添加行.该示例似乎表明它rowt不是null,但仍然rowt IS NOT NULL返回f(并且不会引发异常).

我希望这是我看不到的小事.

select * into rowt from Email where email_email = eml;
RAISE NOTICE '%, rowt IS NOT NULL:%',rowt, rowt IS NOT NULL;
if rowt IS NOT NULL THEN
   RAISE EXCEPTION 'email address, %, already registered.' , eml;
END IF;
Run Code Online (Sandbox Code Playgroud)

输出:

NOTICE:  (7,,,), rowt IS NOT NULL:f

registration 
--------------
    21
(1 row)

CREATE TABLE IF NOT EXISTS Email ( 
   email_email VARCHAR(50) NOT NULL, 
   email_password VARCHAR(50) NOT NULL,
   email_id integer DEFAULT nextval('email_email_id_seq'::regclass) NOT NULL,
   email_person_id integer
);
CREATE OR REPLACE FUNCTION registration( wr text ) RETURNS integer AS $rL$
DECLARE
    eml text;
    pwd text;
    nm text;
    rle text;
    emid integer;
    rowt Email%ROWTYPE;
BEGIN
    eml := getWebVarValue( wr , 'email' );
    select * into rowt from Email where email_email = eml;
    RAISE NOTICE '%, rowt IS NOT NULL:%', rowt, rowt IS NOT NULL;
    IF rowt IS NOT NULL THEN
       RAISE EXCEPTION 'email address, %, already registered.' , eml;
    END IF;
    pwd := getWebVarValue( wr , 'password' );
    IF pwd IS NULL THEN
       RAISE EXCEPTION 'No password specified in registration.';
    END IF;
    INSERT INTO Email VALUES (eml,pwd) RETURNING Email.email_id INTO emid;
    --nm =  getWebVarValue( wr , 'name' );
    --rle = getWebVarValue( wr , 'role' );
    RETURN emid;
END;
$rL$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 11

正如@Pavel提供的那样,检查<row-type> IS NOT NULL不会像您期望的那样工作.它返回TRUEif(且仅当)每个列都是NOT NULL.

测试特殊变量NOT NULL(如@Mike评论):

CREATE OR REPLACE FUNCTION registration(wr text)
  RETURNS integer AS
$rL$
    ...

    SELECT * INTO rowt FROM email WHERE email_email = eml;

    IF FOUND THEN
       RAISE EXCEPTION 'email address, %, already registered.', eml;
    END IF;

    ...
$rL$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

或者你可以在测试中反转你的表达.

IF rowt IS NULL THEN
   -- do nothing
ELSE 
   RAISE EXCEPTION 'email address, %, already registered.' , eml;
END IF;
Run Code Online (Sandbox Code Playgroud)

您找到的任何现有行至少包含一列FOUND,因此NOT NULL只有在找不到任何内容时才会返回rowt IS NULL.

相关答案更多细节:


Pav*_*ule 5

针对ROW类型的NULL测试是特定的:

postgres=# SELECT r, r IS NULL AS "is null", r IS NOT NULL AS "is not null" 
              FROM (VALUES(NULL,NULL),
                          (10, NULL), 
                          (10,10)) r ;
    r    | is null  | is not null 
---------+----------+--------------
 (,)     | t        | f
 (10,)   | f        | f
 (10,10) | f        | t
 (3 rows)
Run Code Online (Sandbox Code Playgroud)

因此,NOT NULL仅当所有字段都不为null时,才返回true。


Pat*_*ick 2

从您的代码可以看出,您希望通过将电子邮件地址插入表中来注册该地址,但前提是该电子邮件地址尚未注册并且提供了密码。对于初学者,您应该更改表定义以反映这些要求:

CREATE TABLE email ( 
    id        serial PRIMARY KEY,
    addr      varchar(50) UNIQUE NOT NULL, 
    passw     varchar(50) NOT NULL,
    person_id integer
);
Run Code Online (Sandbox Code Playgroud)

UNIQUE限制addr意味着 PG 不允许重复的电子邮件地址,因此您不必对此进行测试。您应该在执行插入时测试唯一的违规行为。

对于该函数,我建议您传入电子邮件地址和密码,而不是将业务逻辑放在函数内。像这样,该函数具有较少的依赖性,并且可以更轻松地在其他上下文中重用(例如通过网络应用程序通过其他方式注册电子邮件地址)。创建该函数STRICT可确保其pwd不为空,从而节省您的另一次测试。

CREATE OR REPLACE FUNCTION registration(eml text, pwd text) RETURNS integer AS $rL$
DECLARE
    emid integer;
BEGIN
    INSERT INTO email (addr, passw) VALUES (eml, pwd) RETURNING id INTO emid;
    RETURN emid;
EXCEPTION
    WHEN unique_violation THEN
        RAISE 'Email address % already registered', eml;
        RETURN NULL;
END;
$rL$ LANGUAGE plpgsql STRICT;
Run Code Online (Sandbox Code Playgroud)