在PostgreSQL中存储电子邮件地址的正确数据类型是什么?
我可以使用varchar
(甚至使用text
),但是我想知道电子邮件是否有更具体的数据类型。
在PostgreSQL中存储电子邮件地址的正确数据类型是什么?
我可以使用varchar
(甚至使用text
),但是我想知道电子邮件是否有更具体的数据类型。
Answers:
DOMAIN
小号我认为使用citext
(不区分大小写)不够[1]。使用PostgreSQL,我们可以创建一个自定义域,该域本质上是对类型的一些定义约束。我们可以在citext
类型或之上创建一个域text
。
type=email
规范当前,在RFC5322中指定了对什么是电子邮件地址这个问题的最正确答案。该规范非常复杂[2],以至于一切都破坏了它。HTML5包含针对电子邮件的其他规范,
此要求是对RFC 5322的故意违反,它定义了同时太严格(在“ @”字符之前),太模糊(在“ @”字符之后)和太松散(允许注释)的电子邮件地址语法。 ,空白字符和引号引起的字符串(大多数用户不熟悉的方式)在此处具有实际用途。[...]以下与JavaScript和Perl兼容的正则表达式是上述定义的实现。
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
这可能就是您想要的,如果对HTML5足够好,那么对您也足够好。我们可以在PostgreSQL中直接使用它。我也在citext
这里使用(从技术上讲,这意味着您可以通过删除大写或小写字母来在视觉上简单地进行正则表达式)。
CREATE EXTENSION citext;
CREATE DOMAIN email AS citext
CHECK ( value ~ '^[a-zA-Z0-9.!#$%&''*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$' );
现在您可以...
SELECT 'asdf@foobar.com'::email;
但不是
SELECT 'asdf@foob,,ar.com'::email;
SELECT 'asd@f@foobar.com'::email;
因为这两个都回来了
ERROR: value for domain email violates check constraint "email_check"
因为这也是基于citext
SELECT 'asdf@foobar.com'::email = 'ASdf@fooBAR.com';
默认情况下返回true。
plperlu
/Email::Valid
需要注意的是,有一种更正确的方法,使用会更加复杂plperlu
。如果你需要这种级别的正确性你不想要citext
。Email::Valid
甚至可以检查域是否具有MX记录(例如Email :: Valid的文档中的示例)!首先,添加plperlu(需要超级用户)。
CREATE EXTENSION plperlu;
然后创建函数,注意我们标记为IMMUTABLE
:
CREATE FUNCTION valid_email(text)
RETURNS boolean
LANGUAGE plperlu
IMMUTABLE LEAKPROOF STRICT AS
$$
use Email::Valid;
my $email = shift;
Email::Valid->address($email) or die "Invalid email address: $email\n";
return 'true';
$$;
然后创建域,
CREATE DOMAIN validemail AS text NOT NULL
CONSTRAINT validemail_check CHECK (valid_email(VALUE));
citext
在技术上是错误的。SMTP定义local-part
为区分大小写。但是,再次,这是规范愚蠢的情况。它包含自己的身份危机。规范说local-part
(之前的部分@
)“可能区分大小写”……“必须被视为区分大小写”……但“利用邮箱本地部分的区分大小写会妨碍互操作性,因此不鼓励使用。”这些正则表达式均未对整个电子邮件地址或本地部分或域名强加长度限制。RFC 5322没有指定任何长度限制。这些源于其他协议的限制,例如用于实际发送电子邮件的SMTP协议。RFC 1035确实规定域不得超过63个字符,但在其语法规范中不包括该域。原因是真正的常规语言不能强制执行长度限制,并且不能同时禁止连续的连字符。
a-z
和是否有原因A-Z
?
~
区分大小写,所以您要么(a)~*
不区分大小写,要么(b)在char类中使用大小写字母。
citext
的~
似乎是不区分大小写对我来说,这就是为什么我问。
我总是使用CITEXT
电子邮件,因为(实际上)电子邮件地址不区分大小写,即John@Example.com与john@example.com相同。
与文本相比,设置唯一索引以防止重复也更容易:
-- citext
CREATE TABLE address (
id serial primary key,
email citext UNIQUE,
other_stuff json
);
-- text
CREATE TABLE address (
id serial primary key,
email text,
other_stuff json
);
CREATE UNIQUE INDEX ON address ((lower(email)));
比较电子邮件也更容易,并且不易出错:
SELECT * FROM address WHERE email = 'JOHN@example.com';
相比于:
SELECT * FROM address WHERE lower(email) = lower('JOHN@example.com');
CITEXT
是在名为“ citext”的标准扩展模块中定义的类型,可以通过键入以下内容进行使用:
CREATE EXTENSION citext;
PS text
和varchar
Postgres中的几乎相同,并且使用它不会受到任何惩罚text
。检查此答案:文本和varchar之间的区别
我一直使用,varchar(254)
因为电子邮件地址不能超过254个字符。
参见https://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address
尽管我确实遇到了一些有用的数据类型,但Postgresql没有电子邮件地址的内置类型。
另外,如果您希望在其上添加唯一键,则可能希望添加触发器或一些此类逻辑来标准化电子邮件地址。
特别是,domain
电子邮件地址的一部分(格式为local-part
@ domain
区分大小写,但local-part
必须区分大小写。请参阅http://tools.ietf.org/html/rfc5321#section-2.4
另一个要考虑的问题是,如果您希望以表格形式存储名称和电子邮件地址"Joe Bloggs" <joe.bloggs@hotmail.com>
,在这种情况下,您需要的字符串要长于254个字符,并且您将无法有意义地使用唯一约束。我不会这样做,建议分开存储名称和电子邮件地址。在您的表示层中始终可以使用这种格式打印漂亮的地址。
@
)。
@
)=320。也许我误解了它。