Bir e-posta adresini PostgreSQL'de saklamanın en iyi yolu nedir?


40

PostgreSQL'de e-posta adreslerini saklamak için doğru veri türü ne olurdu?

Kullanabilir varchar(veya hatta text), ancak e-postalar için daha belirli bir veri türü olup olmadığını merak ediyorum.

Yanıtlar:


38

Özel DOMAINs

Kullanmanın citext(büyük / küçük harfe duyarsız) yeterli olduğunu sanmıyorum [1] . PostgreSQL kullanarak , aslında bir tür üzerinde tanımlanmış bazı kısıtlamalar olan özel bir etki alanı oluşturabiliriz . Örneğin tür üzerinde veya üzerinde bir etki alanı oluşturabiliriz .citexttext

HTML5 type=emailspec kullanma

Şu anda bir e-posta adresi nedir sorusuna en doğru cevap RFC5322'de belirtilmiştir . Bu özellik delicesine karmaşık [2] , öyle ki her şey onu kıracak. HTML5 e-posta için farklı bir spec içeren ,

Bu gereksinim, aynı anda çok katı ("@" karakterinden önce), çok belirsiz ("@" karakterinden sonra) ve çok gevşek (yorum yapılmasına izin veren) e-posta adreslerinin sözdizimini tanımlayan kasıtlı bir RFC 5322 ihlalidir. , boşluk karakterleri ve alıntılanan dizeler çoğu kullanıcının bilmediği yöntemlerle burada pratik olarak kullanılmalıdır. [...] Aşağıdaki JavaScript ve Perl uyumlu normal ifade, yukarıdaki tanımın bir uygulamasıdır.

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

İstediğiniz şey bu olabilir ve HTML5 için yeterince iyiyse, muhtemelen sizin için yeterince iyidir. Bunu doğrudan PostgreSQL'de kullanabiliriz. Ayrıca citextburada da kullanıyorum (teknik olarak basit veya küçük harfleri çıkararak basitçe biraz regex yapabilirsiniz).

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

Şimdi yapabilirsin ...

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

Ama değil

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

Çünkü ikisi de geri döndü

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

Çünkü bu da citext'e dayanıyor

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

varsayılan olarak true değerini döndürür.

plperlu/ KullanarakEmail::Valid

Önemli bir not olarak, bunu yapmanın çok daha karmaşık bir yöntemi vardır plperlu. Eğer doğruluğu bu düzeyde gerekiyorsa yapmak değil istiyorum citext. Email::Validetki alanı MX kaydının olup olmadığını bile kontrol edebilir (örn. Email :: Valid'deki dokümanlar)! İlk önce, plperlu ekleyin (süper kullanıcı gerektirir).

CREATE EXTENSION plperlu;

Ardından fonksiyonu yaratın, aşağıdaki gibi işaretlediğimize dikkat edin 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';
$$;

Ardından etki alanını oluşturun ,

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

Dipnotlar

  1. Kullanmak citextteknik olarak yanlıştır. SMTP, büyük / local-partküçük harfe duyarlı olarak tanımlar . Ancak, yine de, bu aptallık spec bir durumdur . Kendi kimlik krizlerini içeriyor. Spesifikasyonda local-part(önceki bölüm @) "büyük / küçük harfe duyarlı olabilir" ... "büyük / küçük harf duyarlı olarak değerlendirilmeli" "ve yine de" posta kutusunun yerel parçalarının büyük / küçük harf duyarlılığından yararlanılması birlikte çalışabilirliği engelliyor ve cesaretini kırıyor "diyor.
  2. Bir e-posta adresi için verilen özellik o kadar karmaşık ki, kendi kendine yeten bile değil. Karmaşık gerçekten bir understatement, spec yapanlar onu bile anlamadı. . Regular-expression.info adresindeki dokümanlardan

    Bu regex'lerin hiçbiri genel e-posta adresinde veya yerel kısımda veya etki alanı adlarında uzunluk sınırlarını zorlamaz. RFC 5322, herhangi bir uzunluk sınırlaması belirtmiyor. Bunlar, gerçekten e-posta göndermek için SMTP protokolü gibi diğer protokollerdeki sınırlamalardan kaynaklanmaktadır. RFC 1035, etki alanlarının 63 karakter veya daha kısa olması gerektiğini belirtir, ancak sözdizimi belirtiminde yer almaz. Bunun nedeni, gerçek bir normal dilin bir uzunluk sınırını zorlayamaması ve aynı anda ardışık kısa çizgilere izin vermemesidir.


1
W3.org bağlantısı koptu; burada alternatif bir kaynak var: html.spec.whatwg.org/multipage/…
MaxGabriel

@MaxGabriel teşekkürler stick etrafında, düzenleme izinlerini yakında alacaksınız sabitlerim.
Evan Carroll

İkisine birden sahip bir nedeni var mı a-zve A-Zkarakter sınıflarında?
xehpuk

@ xehpuk iyi, çünkü ~büyük küçük harf duyarlı olduğundan , (a) büyük / ~*küçük harfe duyarsız kullanmak veya (b) karakter sınıfında büyük ve küçük harf kullanmak zorundasınız.
Evan Carroll

citext'ler ~yüzden soruyorum olduğunu, küçük harf duyarsız bana gibi görünüyor.
xehpuk

46

Her zaman CITEXTe-posta için kullanıyorum , çünkü bir e-posta adresi (uygulamada) büyük / küçük harfe duyarsız , yani John@Example.com john@example.com ile aynı.

Ayrıca, yinelenenleri engellemek için metne kıyasla benzersiz bir dizin oluşturmak daha kolaydır:

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

E-postaları karşılaştırmak da daha kolaydır ve hatalara daha az eğilimlidir:

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

ile kıyaslandığında:

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

CITEXT"citext" adlı standart bir uzantı modülünde tanımlanan ve aşağıdakileri yazarak kullanılabilen bir türdür :

CREATE EXTENSION citext;

PS textve varcharPostgres'te neredeyse aynıdır ve textbeklediğiniz gibi kullanmanın bir cezası yoktur . Bu cevabı kontrol et: Metin ve varchar arasındaki fark


10

Her zaman varchar(254)bir e-posta adresi olarak kullanıyorum , 254 karakterden uzun olamaz.

Bkz. Https://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-adress

Postgresql'in e-posta adresleri için yerleşik bir türü yok, ancak bazı katkıda bulunan veri türleriyle karşılaştım.

Ayrıca, üzerine benzersiz bir anahtar eklemek istemeniz durumunda e-posta adreslerini standart hale getirmek için bir tetikleyici veya bu tür bir mantık eklemek isteyebilirsiniz.

Özellikle, domaine-posta adresinin ( local-part@ şeklindeki domainkısmı büyük / küçük harfe duyarsızken local-partküçük harfe duyarlı olarak değerlendirilmelidir. Bkz. Http://tools.ietf.org/html/rfc5321#section-2.4

Diğer bir husus, isimleri ve e-posta adreslerini formda saklamak istiyorsanız "Joe Bloggs" <joe.bloggs@hotmail.com>, bu durumda 254 karakterden daha uzun bir dizgeye ihtiyacınız olacak ve benzersiz bir kısıtlamayı anlamlı bir şekilde kullanamayacaksınız. Bunu yapmaz ve ad ve e-posta adresini ayrı ayrı saklamanızı öneririm. Sunum katmanınızda bu formatta hoş baskı adresleri her zaman mümkündür.


4.5.3.1'e göre . Boyut Sınırları ve Minimumlar , maksimum uzunluk 320 karakterdir (dahil @).
Andriy M,

1
@AndriyM Referans yazan bölümde 320 yazan hiçbir şey yok. Yine de yanlış; tools.ietf.org/html/rfc5321#section-4.5.3.1.3 , bir yolun maksimum uzunluğunun 256 karakter olduğunu ve bunun etrafındaki "<" ve ">" en fazla 254 değerini içermesi gerektiğini belirtir.
Colin Hart

4.5.3.1.1 ("Bir kullanıcı adının veya diğer yerel bölümün maksimum toplam uzunluğu 64 oktet") ve 4.5.3.1.2 ("Bir alan adının maksimum toplam uzunluğu) esas alınarak 320’ye maksimum olarak ulaşdım veya sayı 255 oktettir "). Yani, 64 + 255 + 1 (the @) = 320. Belki de yanlış yorumluyorum.
Andriy M

3
@AndriyM Bağlantılı olduğum sorunun cevabını kabul et. Hepsini açıklar. Kesinlikle 254 ve 320 değil.
Colin, Hart,

3

Bir CONSTRAINT onay denetimi kullanmakla ilgileniyor olabilirsiniz (muhtemelen daha kolay, ancak istediğinizden daha fazlasını reddedebilir veya burada ve burada tartışılan bir FUNCTION işlevini kullanırsınız . PostgreSQL bile yerel bir IP adres tipine sahip olsa da, burada bir e-posta veri türü için pgfoundry üzerinde bir proje var, ancak bu konuda bulduğum en iyi şey bir e-posta alanı.. Etki alanı bir kontrol kısıtlamasından daha iyidir, çünkü değiştirirseniz, etki alanı tanımında yalnızca bir kez yapmanız gerekir ve tüm kontrol kısıtlamalarınızı değiştiren ebeveyn-çocuk tablolarındaki izleri takip etmemelisiniz. Etki alanları gerçekten harika - veri türleri gibi, ancak uygulanması daha kolaydır. Onları Firebird'de kullandım - Oracle'da bile yok!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.