วิธีที่ดีที่สุดในการจัดเก็บที่อยู่อีเมลใน PostgreSQL คืออะไร


40

ประเภทข้อมูลที่เหมาะสมในการจัดเก็บที่อยู่อีเมลใน PostgreSQL คืออะไร

ฉันสามารถใช้varchar(หรือแม้กระทั่งtext) แต่ฉันสงสัยว่ามีประเภทข้อมูลเฉพาะสำหรับอีเมล

คำตอบ:


38

ที่กำหนดเองDOMAINs

ผมไม่คิดว่าการใช้citext(กรณีตาย) ก็พอ[1] ใช้ PostgreSQL เราสามารถสร้างโดเมนที่กำหนดเองซึ่งเป็นหลักข้อ จำกัด ที่กำหนดไว้บางกว่าชนิด เราสามารถสร้างโดเมนเช่นมากกว่าชนิดหรือมากกว่าcitexttext

ใช้type=emailข้อมูลจำเพาะHTML5

ปัจจุบันคำตอบที่ถูกต้องที่สุดสำหรับคำถามที่ว่าอะไรคือที่อยู่อีเมลที่ถูกระบุไว้ใน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ที่นี่ (ซึ่งในทางเทคนิคหมายความว่าคุณสามารถ regex นิดหน่อยด้วยการลบตัวพิมพ์เล็กหรือตัวพิมพ์เล็ก)

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

ผลตอบแทนจริงตามค่าเริ่มต้น

ใช้plperlu/Email::Valid

plperluเป็นบันทึกที่สำคัญมีวิธีการที่ถูกต้องมากขึ้นของการทำเช่นนี้ที่อยู่ห่างไกลที่ซับซ้อนมากขึ้นโดยใช้ ถ้าคุณต้องการระดับของความถูกต้องนี้คุณจะไม่citextต้องการ Email::Validสามารถตรวจสอบว่าโดเมนมีระเบียน MX (ตัวอย่างในเอกสารของอีเมล :: ถูกต้อง)! ก่อนอื่นให้เพิ่ม plperlu (ต้องการ superuser)

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

เชิงอรรถ

  1. การใช้citextผิดเทคนิค SMTP กำหนดlocal-partว่าเป็นตัวพิมพ์เล็กหรือใหญ่ แต่นี่เป็นกรณีของข้อมูลจำเพาะที่โง่ มันมีวิกฤตตัวตนของตัวเอง ข้อมูลจำเพาะกล่าวว่าlocal-part(ส่วนก่อนหน้า@) "อาจต้องตรงตามตัวพิมพ์ใหญ่ - เล็ก" ... "ต้องถือว่าเป็นตัวพิมพ์เล็กและตัวพิมพ์ใหญ่" ... และยัง "ใช้ประโยชน์จากตัวพิมพ์เล็กและตัวพิมพ์เล็กของกล่องจดหมาย
  2. ข้อมูลจำเพาะสำหรับที่อยู่อีเมลนั้นซับซ้อนไม่ได้มีอยู่ในตัวเอง คอมเพล็กซ์เป็นคำพูดที่แท้จริงผู้สร้างสเป็คไม่เข้าใจแม้แต่น้อย . จากเอกสารบน regular-expression.info

    regexes เหล่านี้ไม่บังคับใช้การจำกัดความยาวของที่อยู่อีเมลโดยรวมหรือส่วนในท้องถิ่นหรือชื่อโดเมน RFC 5322 ไม่ได้ระบุข้อจำกัดความยาวใด ๆ ต้นกำเนิดมาจากข้อ จำกัด ในโปรโตคอลอื่นเช่นโปรโตคอล SMTP สำหรับการส่งอีเมลจริง RFC 1035 ระบุว่าโดเมนต้องมีความยาวไม่เกิน 63 ตัวอักษร แต่ไม่รวมอยู่ในข้อมูลจำเพาะทางไวยากรณ์ เหตุผลคือภาษาจริงที่แท้จริงไม่สามารถบังคับใช้ความยาวสูงสุดและไม่อนุญาตให้ใช้เครื่องหมายขีดกลางต่อเนื่องในเวลาเดียวกัน


1
ลิงก์ W3.org เสีย นี่คือแหล่งสำรอง: html.spec.whatwg.org/multipage/ …
MaxGabriel

@ MaxGabriel ขอบคุณติด ๆ คุณจะได้รับการแก้ไข perms เร็วพอที่ฉันจะได้รับการแก้ไขในนั้น
Evan Carroll

มีเหตุผลที่จะมีทั้งa-zและA-Zในชั้นเรียนตัวอักษร?
xehpuk

@ xehpuk ดีเพราะ~เป็นกรณี ๆ ไปคุณต้อง (a) ใช้~*case insensitive หรือ (b) มีตัวอักษรพิมพ์ใหญ่และพิมพ์เล็กใน char-class
Evan Carroll

citext's ~น่าจะเป็นกรณีตายให้ฉันว่าทำไมฉันขอ
xehpuk

46

ฉันมักจะใช้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


10

ฉันมักจะใช้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 อักขระและคุณจะไม่สามารถใช้ข้อ จำกัด ที่มีความหมายได้ ฉันจะไม่ทำเช่นนี้และแนะนำการจัดเก็บชื่อและที่อยู่อีเมลแยกจากกัน ที่อยู่การพิมพ์ที่น่ารักในรูปแบบนี้จะเป็นไปได้เสมอในเลเยอร์การนำเสนอของคุณ


ตาม4.5.3.1 ขนาด จำกัด และขั้นต่ำความยาวสูงสุดคือ 320 ตัวอักษร (รวมถึง@)
Andriy M

1
@AndriyM ไม่มีอะไรในส่วนอ้างอิงที่บอกว่า 320 และมันก็ผิดอยู่ดี tools.ietf.org/html/rfc5321#section-4.5.3.1.3ระบุว่าความยาวสูงสุดของพา ธ คือ 256 อักขระและต้องมีการล้อมรอบ "<" และ ">" ทำให้สูงสุด 254
Colin 't Hart

ฉันมาที่ 320 โดยมากที่สุดโดยอิงจาก 4.5.3.1.1 ("ความยาวรวมสูงสุดของชื่อผู้ใช้หรือส่วนอื่น ๆ ในท้องถิ่นคือ 64 octets") และ 4.5.3.1.2 ("ความยาวรวมสูงสุดของชื่อโดเมน หรือหมายเลขคือ 255 octets ") ดังนั้น 64 + 255 + 1 (the @) = 320 บางทีฉันอาจตีความผิด
Andriy M

3
@AndriyM อ่านคำตอบที่ยอมรับสำหรับคำถามที่ฉันเชื่อมโยง มันอธิบายได้ทั้งหมด มันคือ 254 และไม่ใช่ 320
Colin 't Hart

3

คุณอาจสนใจใช้เช็คข้อ จำกัด (อาจจะง่ายกว่า แต่อาจปฏิเสธมากกว่าที่คุณต้องการหรือคุณใช้ฟังก์ชั่นที่กล่าวถึงที่นี่และที่นี่โดยพื้นฐานแล้วมันเกี่ยวกับการแลกเปลี่ยนระหว่างความเฉพาะเจาะจงและความง่ายในการใช้งาน แม้ว่า PostgreSQL จะมีประเภทที่อยู่ IP ดั้งเดิม แต่มีโครงการใน pgfoundry สำหรับประเภทข้อมูลอีเมลที่นี่อย่างไรก็ตามสิ่งที่ดีที่สุดที่ฉันพบคือโดเมนอีเมล. โดเมนนั้นดีกว่าข้อ จำกัด การตรวจสอบเพราะถ้าคุณเปลี่ยนคุณต้องทำเพียงครั้งเดียวในการกำหนดโดเมนและไม่ปฏิบัติตามเส้นทางในตารางพ่อแม่และลูกเปลี่ยนแปลงข้อ จำกัด การตรวจสอบทั้งหมดของคุณ โดเมนนั้นเจ๋งจริงๆ - เหมือนประเภทข้อมูล แต่ง่ายต่อการใช้งาน ฉันใช้มันใน Firebird - Oracle ไม่มีแม้กระทั่งพวกเขา!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.