Cách tốt nhất để lưu trữ một địa chỉ email trong PostgreSQL là gì?


40

Điều gì sẽ là kiểu dữ liệu phù hợp để lưu trữ địa chỉ email trong PostgreSQL?

Tôi có thể sử dụng varchar(hoặc thậm chí text), nhưng tôi tự hỏi liệu có loại dữ liệu cụ thể hơn cho email không.

Câu trả lời:


38

Tùy chỉnh DOMAINs

Tôi không nghĩ sử dụng citext(không phân biệt chữ hoa chữ thường) là đủ [1] . Sử dụng PostgreQuery, chúng ta có thể tạo một miền tùy chỉnh về cơ bản là một số ràng buộc được xác định đối với một loại . Chúng ta có thể tạo một tên miền chẳng hạn qua citextloại hoặc hơn text.

Sử dụng type=emailthông số HTML5

Hiện tại câu trả lời đúng nhất cho câu hỏi địa chỉ email là gì được chỉ định trong RFC5322 . Thông số kỹ thuật đó cực kỳ phức tạp [2] , đến nỗi mọi thứ phá vỡ nó. HTML5 chứa một thông số kỹ thuật khác nhau cho email ,

Yêu cầu này là vi phạm cố ý RFC 5322, định nghĩa cú pháp cho các địa chỉ email đồng thời quá nghiêm ngặt (trước ký tự "@"), quá mơ hồ (sau ký tự "@") và quá lỏng lẻo (cho phép nhận xét , các ký tự khoảng trắng và các chuỗi trích dẫn trong cách cư xử không quen thuộc với hầu hết người dùng) sẽ được sử dụng thực tế ở đây. [...] Biểu thức chính quy tương thích với JavaScript và Perl sau đây là cách triển khai định nghĩa trên.

/^[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])?)*$/

Đây có thể là những gì bạn muốn và nếu nó đủ tốt cho HTML5, thì nó có thể đủ tốt cho bạn. Chúng ta có thể sử dụng điều đó trực tiếp trong PostgreSQL. Tôi cũng sử dụng citextở đây (về mặt kỹ thuật có nghĩa là bạn có thể đơn giản là regex một chút trực quan bằng cách loại bỏ chữ hoa hoặc chữ thường).

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])?)*$' );

Bây giờ bạn có thể làm ...

SELECT 'asdf@foobar.com'::email;

Nhưng không

SELECT 'asdf@foob,,ar.com'::email;
SELECT 'asd@f@foobar.com'::email;

Bởi vì cả hai trở về

ERROR:  value for domain email violates check constraint "email_check"

Bởi vì điều này cũng dựa trên citext

SELECT 'asdf@foobar.com'::email = 'ASdf@fooBAR.com';

trả về đúng theo mặc định.

Sử dụng plperlu/Email::Valid

Là một lưu ý quan trọng, có một phương pháp chính xác hơn để làm điều này phức tạp hơn nhiều bằng cách sử dụng plperlu. Nếu bạn cần mức độ chính xác này, bạn không muốn citext. Email::Validthậm chí có thể kiểm tra xem tên miền có bản ghi MX không (ví dụ trong tài liệu của Email :: Hợp lệ)! Đầu tiên, thêm plperlu (yêu cầu siêu người dùng).

CREATE EXTENSION plperlu;

Sau đó tạo hàm , thông báo chúng tôi đánh dấu là 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';
$$;

Sau đó tạo miền ,

CREATE DOMAIN validemail AS text NOT NULL
  CONSTRAINT validemail_check CHECK (valid_email(VALUE));

Chú thích

  1. Sử dụng citextlà sai kỹ thuật. SMTP được định nghĩa local-partlà trường hợp nhạy cảm. Nhưng, một lần nữa, đây là một trường hợp thông số kỹ thuật là ngu ngốc. Nó chứa các cuộc khủng hoảng bản sắc riêng của nó. Thông số kỹ thuật cho biết local-part(phần trước @) "CÓ THỂ phân biệt chữ hoa chữ thường" ... "PHẢI được coi là phân biệt chữ hoa chữ thường" ... và "khai thác độ nhạy trường hợp của các bộ phận cục bộ hộp thư cản trở khả năng tương tác và không được khuyến khích."
  2. Thông số kỹ thuật cho một địa chỉ email rất phức tạp, nó thậm chí không khép kín. Phức tạp thực sự là một cách đánh giá thấp, những người làm cho thông số kỹ thuật thậm chí không hiểu nó. . Từ các tài liệu trên normal-expression.info

    Cả hai biểu thức này đều thực thi giới hạn độ dài đối với địa chỉ email tổng thể hoặc phần cục bộ hoặc tên miền. RFC 5322 không chỉ định bất kỳ giới hạn độ dài. Những điều đó xuất phát từ những hạn chế trong các giao thức khác như giao thức SMTP để thực sự gửi email. RFC 1035 tuyên bố rằng các tên miền phải có 63 ký tự trở xuống, nhưng không bao gồm tên miền trong đặc tả cú pháp của nó. Lý do là một ngôn ngữ thông thường thực sự có thể thực thi giới hạn độ dài và không cho phép các dấu gạch nối liên tiếp cùng một lúc.


1
Liên kết W3.org bị hỏng; đây là một nguồn thay thế: html.spec.whatwg.org/multipage/ Kẻ
MaxGabriel

@MaxGabriel cảm ơn bạn, bạn sẽ sớm nhận được bản chỉnh sửa. Tôi sẽ sửa nó trong đó.
Evan Carroll

Có một lý do để có cả hai a-zA-Ztrong các lớp nhân vật?
xehpuk

@xehpuk tốt, vì phân biệt chữ hoa chữ thường, ~bạn phải (a) sử dụng ~*chữ không phân biệt chữ hoa chữ thường hoặc (b) có chữ hoa và chữ thường trong lớp char.
Evan Carroll

citextTôi ~dường như không nhạy cảm với tôi, đó là lý do tại sao tôi hỏi.
xehpuk

46

Tôi luôn luôn sử dụng CITEXTcho email, bởi vì một địa chỉ email là (trong thực tế) trường hợp không nhạy cảm , tức là John@Example.com giống như john@example.com.

Cũng dễ dàng hơn để thiết lập một chỉ mục duy nhất để ngăn ngừa trùng lặp, so với văn bản:

-- 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)));

So sánh email cũng dễ dàng hơn và ít bị lỗi hơn:

SELECT * FROM address WHERE email = 'JOHN@example.com';

so với:

SELECT * FROM address WHERE lower(email) = lower('JOHN@example.com');

CITEXTlà một loại được xác định trong một mô-đun mở rộng tiêu chuẩn có tên là "citext" và có sẵn bằng cách gõ:

CREATE EXTENSION citext;

PS textvarcharhầu như giống nhau trong Postgres và không có hình phạt cho việc sử dụng textnhư người ta có thể mong đợi. Kiểm tra câu trả lời này: Sự khác biệt giữa văn bản và varchar


10

Tôi luôn sử dụng varchar(254)làm địa chỉ email có thể không dài hơn 254 ký tự.

Xem https://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address

Postgresql không có loại tích hợp cho địa chỉ email, mặc dù tôi đã bắt gặp một số loại dữ liệu đóng góp.

Ngoài ra, bạn có thể muốn thêm một trình kích hoạt hoặc một số logic như vậy để chuẩn hóa địa chỉ email trong trường hợp bạn muốn thêm một khóa duy nhất vào nó.

Cụ thể, domainmột phần của địa chỉ email (có dạng local-part@ domainkhông phân biệt chữ hoa chữ thường trong khi local-partphải được coi là phân biệt chữ hoa chữ thường . Xem http://tools.ietf.org/html/rfc5321#section-2.4

Một cân nhắc khác là nếu bạn muốn lưu trữ tên và địa chỉ email trong biểu mẫu "Joe Bloggs" <joe.bloggs@hotmail.com>, trong trường hợp đó bạn cần một chuỗi dài hơn 254 ký tự và bạn sẽ không thể sử dụng một ràng buộc duy nhất. Tôi sẽ không làm điều này và đề nghị lưu trữ tên và địa chỉ email riêng biệt. Địa chỉ in đẹp ở định dạng này luôn có thể có trong lớp trình bày của bạn.


Theo 4.5.3.1. Giới hạn kích thước và tối thiểu , độ dài tối đa là 320 ký tự (bao gồm cả @).
Andriy M

1
@AndriyM Không có gì trong phần tham chiếu có ghi 320. Và dù sao thì điều đó cũng sai; tools.ietf.org/html/rfc5321#section-4.5.3.1.3 nói rằng độ dài tối đa của một đường dẫn là 256 ký tự và phải bao gồm "<" và ">" bao quanh tối đa là 254.
Colin 't Hart

Tôi đạt tối đa 320 dựa trên 4.5.3.1.1 ("Tổng chiều dài tối đa của tên người dùng hoặc phần cục bộ khác là 64 octet") và 4.5.3.1.2 ("Tổng chiều dài tối đa của một tên miền hoặc số là 255 octet "). Vì vậy, 64 + 255 + 1 (the @) = 320. Có lẽ tôi đang hiểu sai về nó.
Andriy M

3
@AndriyM Đọc câu trả lời được chấp nhận cho câu hỏi tôi liên kết đến. Nó giải thích tất cả. Đó chắc chắn là 254, và không phải 320.
Colin 't Hart

3

Bạn có thể quan tâm đến việc sử dụng kiểm tra CONSTRAINT (có thể dễ dàng hơn, nhưng có thể từ chối nhiều hơn bạn muốn hoặc bạn sử dụng CHỨC NĂNG, thảo luận ở đâyở đây . Về cơ bản, đó là tất cả về sự đánh đổi giữa tính cụ thể và dễ thực hiện. Mặc dù, PostgreSQL thậm chí có một loại địa chỉ IP gốc, nhưng có một dự án trên pgfoundry cho một loại dữ liệu email ở đây . Tuy nhiên, điều tốt nhất tôi tìm thấy về đây là một tên miền email. Tên miền tốt hơn ràng buộc kiểm tra bởi vì nếu bạn thay đổi nó, bạn chỉ phải thực hiện một lần trong định nghĩa miền và không theo dõi các bảng cha-con thay đổi tất cả các ràng buộc kiểm tra của bạn. Tên miền thực sự tuyệt vời - giống như kiểu dữ liệu, nhưng đơn giản hơn để thực hiện. Tôi đã sử dụng chúng trong Firebird - Oracle thậm chí không có chúng!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.