我有一个函数registration()
,它应该在某些情况下向表中添加一行。我已经包含了一段代码和来自通话的输出。
如果select *
返回非空表行(它根据RAISE NOTICE
执行),我想引发异常而不添加行。该示例似乎表明rowt
不为空,但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;
输出:
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;
答案 0 :(得分:11)
作为@Pavel provided,支票<row-type> IS NOT NULL
并不像您期望的那样有效。如果(且仅当) 每一列 为非NULL,它将返回TRUE
。
改为测试特殊变量FOUND
(如@Mike commented):
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;
或你可以在测试中反转你的表达。
IF rowt IS NULL THEN
-- do nothing
ELSE
RAISE EXCEPTION 'email address, %, already registered.' , eml;
END IF;
您找到的任何现有行至少包含一列NOT NULL
,因此如果找不到任何内容,rowt IS NULL
只会返回TRUE
。
相关答案以及更多详情:
答案 1 :(得分:4)
对于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)
所以NOT NULL
仅在所有字段都不为空时才返回true。
答案 2 :(得分:1)
从您的代码中可以看出,您希望通过将电子邮件地址插入表中来注册电子邮件地址,但前提是电子邮件地址尚未注册并且提供了密码。对于初学者,您应该更改表定义以反映这些要求:
CREATE TABLE email (
id serial PRIMARY KEY,
addr varchar(50) UNIQUE NOT NULL,
passw varchar(50) NOT NULL,
person_id integer
);
UNIQUE
上的addr
约束意味着PG不允许重复的电子邮件地址,因此您不必为此进行测试。您应该在执行插入时测试唯一的违规。
对于该功能,我建议您传递电子邮件地址和密码,而不是将业务逻辑放在函数中。像这样,该函数具有较少的依赖性,并且可以更容易地在其他上下文中重复使用(例如通过您的Web应用程序通过其他方式注册电子邮件地址)。使函数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;
答案 3 :(得分:0)
您只想测试该电子邮件的行是否存在。
这可以通过EXISTS
子查询表达式简单地实现:
IF EXISTS(SELECT 1 FROM email WHERE email_email = eml) THEN
RAISE EXCEPTION 'email address, %, already registered.', eml;
END IF;
特殊变量FOUND
也可以正常工作,但当您想要使用找到的行中的某些字段时,它会有更多的价值。
一般来说,<row-type> IS [ NOT ] [ DISTINCT FROM ] NULL
有特殊规则,并不总是相互反转(如@Pavel所述);有三种不同的方法可以对某种方法进行测试
未知状态:
SELECT r,
r IS NULL AS "is null",
r IS NOT NULL AS "is not null",
r IS DISTINCT FROM NULL AS "is distinct from null"
FROM (
VALUES
(ROW(10::int, 10::int)),
(ROW(10::int, NULL::int)),
(ROW(NULL::int, NULL::int)),
(NULL)
) AS s(r);
-- R IS NULL IS NOT NULL IS DISTINCT FROM NULL
-----------------------------------------------------------------
-- '(10,10)' 'f' 't' 't'
-- '(10,)' 'f' 'f' 't'
-- '(,)' 't' 'f' 't'
-- NULL 't' 'f' 'f'
Note:如果表达式是行值,那么当行表达式为空时
IS NULL
为真所有行的字段为空时为strong>或** **,而当行表达式本身为非空时,IS NOT NULL
为真并且所有行的字段都是非空的。由于此行为,IS NULL
和IS NOT NULL
并不总是返回行值表达式的反向结果,即包含NULL
和非空值的行值表达式将返回两个测试都是假的。 此定义符合SQL标准,并且是对8.2之前的PostgreSQL版本所表现出的不一致行为的更改。
此外,当有人使用复合类型而不是行构造函数时,操作符的处理会有一些变化:
Note:如果结果取决于比较两个
NULL
值或NULL
和非{{1},则SQL规范要求按行进行比较以返回NULL
}}。 PostgreSQL仅在比较两个行构造函数的结果或将行构造函数与子查询的输出进行比较时执行此操作(如第9.22节所述)。在其他上下文 1 中,比较两个复合类型值,两个NULL
字段值被视为相等,并且NULL
被认为大于非NULL
。这对于复合类型具有一致的排序和索引行为是必要的。
1 虽然我找不到任何查询,但这样就可以了。
答案 4 :(得分:0)
我遇到了同样的问题并通过强制转换为::text
( arr[1]::text is not null
)在SQL select中访问复合内的某个数组/记录类型与此类似:
select
arr,
arr[1] is not null as nn,
arr[1]::text as as_txt,
arr[1]::text is not null as as_txt_nn
from ... -- "...": some composite type and an array type for it must exist
row| arr nn as_txt as_txt_nn
===| ------- ----- ------ ---------
1 | {(1,a)} true (1,a) true
2 | {(1,NULL)} false (1,NULL) true
3 | {NULL} false <NULL> false
-- hint: "<NULL>" is the null value representation of
-- your sql execution environment
所以as_txt_nn
条件正确地检查问题,以区分与问题相关的第2行和第3行,如果第一个数组合成为空或给定。
nn
条件在某种程度上表现出来(如前面的帖子所述),如果所有复合列都是{{1,则仅返回true
}} 强>
它也适用于PGPLSQL函数。