คุณไม่จำเป็นต้องมีทริกเกอร์หรือ PL / pgSQL เลย
คุณไม่จำเป็นต้องมี DEFERRABLE
ข้อ จำกัด ด้วยซ้ำ
และคุณไม่จำเป็นต้องเก็บข้อมูลใด ๆ ซ้ำซ้อน
รวม ID ของอีเมลที่ใช้งานอยู่ในusers
ตารางทำให้มีการอ้างอิงซึ่งกันและกัน บางคนอาจคิดว่าเราจำเป็นต้องมีDEFERRABLE
ข้อ จำกัด ในการแก้ปัญหาไก่และไข่ในการแทรกผู้ใช้และอีเมลที่ใช้งานของเขา แต่ใช้ CTE ที่ปรับเปลี่ยนข้อมูลเราไม่จำเป็นต้องทำเช่นนั้น
สิ่งนี้จะบังคับให้มีอีเมลที่ใช้งานอยู่หนึ่งอีเมลต่อผู้ใช้หนึ่งรายตลอดเวลา:
CREATE TABLE users (
user_id serial PRIMARY KEY
, username text NOT NULL
, email_id int NOT NULL -- FK to active email, constraint added below
);
CREATE TABLE email (
email_id serial PRIMARY KEY
, user_id int NOT NULL REFERENCES users ON DELETE CASCADE ON UPDATE CASCADE
, email text NOT NULL
, CONSTRAINT email_fk_uni UNIQUE(user_id, email_id) -- for FK constraint below
);
ALTER TABLE users ADD CONSTRAINT active_email_fkey
FOREIGN KEY (user_id, email_id) REFERENCES email(user_id, email_id);
ลบNOT NULL
ข้อ จำกัด ออกจากusers.email_id
เพื่อให้เป็น "อีเมลที่ใช้งานได้สูงสุด" (คุณยังคงสามารถจัดเก็บอีเมลได้หลายรายการต่อผู้ใช้ แต่ไม่มีใครใช้งานเลย ")
คุณสามารถทำให้active_email_fkey
DEFERRABLE
การอนุญาตให้ระยะเวลาเพิ่มขึ้น (ผู้ใช้และอีเมล์แทรกในคำสั่งแยกเดียวกันการทำธุรกรรม) แต่ที่ไม่จำเป็น
ฉันใส่ user_id
ครั้งแรกในUNIQUE
ข้อ จำกัดemail_fk_uni
ในการเพิ่มประสิทธิภาพความครอบคลุมของดัชนี รายละเอียด:
มุมมองทางเลือก:
CREATE VIEW user_with_active_email AS
SELECT * FROM users JOIN email USING (user_id, email_id);
นี่คือวิธีที่คุณจะแทรกผู้ใช้ใหม่ด้วยอีเมลที่ใช้งาน (ตามต้องการ):
WITH new_data(username, email) AS (
VALUES
('usr1', 'abc@d.com') -- new users with *1* active email
, ('usr2', 'def3@d.com')
, ('usr3', 'ghi1@d.com')
)
, u AS (
INSERT INTO users(username, email_id)
SELECT n.username, nextval('email_email_id_seq'::regclass)
FROM new_data n
RETURNING *
)
INSERT INTO email(email_id, user_id, email)
SELECT u.email_id, u.user_id, n.email
FROM u
JOIN new_data n USING (username);
ความยากลำบากที่เฉพาะเจาะจงคือว่าเรามีค่าuser_id
มิได้email_id
จะเริ่มต้นด้วย SEQUENCE
ทั้งสองเป็นหมายเลขที่ให้ไว้จากนั้น ไม่สามารถแก้ไขได้ด้วยRETURNING
ประโยคเดียว(ปัญหาไก่และไข่อื่น) ทางแก้คือnextval()
เป็นอธิบายในรายละเอียดในคำตอบที่เชื่อมโยงด้านล่าง
หากคุณไม่ทราบชื่อของลำดับที่แนบมาสำหรับserial
คอลัมน์email.email_id
คุณสามารถแทนที่:
nextval('email_email_id_seq'::regclass)
กับ
nextval(pg_get_serial_sequence('email', 'email_id'))
นี่คือวิธีเพิ่มอีเมล "ที่ใช้งาน" ใหม่:
WITH e AS (
INSERT INTO email (user_id, email)
VALUES (3, 'new_active@d.com')
RETURNING *
)
UPDATE users u
SET email_id = e.email_id
FROM e
WHERE u.user_id = e.user_id;
ซอ Fiddle
คุณอาจแค็ปซูลคำสั่ง SQL ในฟังก์ชั่นฝั่งเซิร์ฟเวอร์ถ้าคำสั่ง ORM แบบง่าย ๆ นั้นไม่ฉลาดพอที่จะรับมือกับสิ่งนี้
มีความเกี่ยวข้องอย่างใกล้ชิดพร้อมคำอธิบายที่เพียงพอ:
ยังเกี่ยวข้องกับ:
เกี่ยวกับDEFERRABLE
ข้อ จำกัด :
เกี่ยวกับnextval()
และpg_get_serial_sequence()
: