it-swarm.asia

القيد الفريد PostgreSQL متعدد الأعمدة والقيم الفارغة

لدي جدول مثل ما يلي:

create table my_table (
    id   int8 not null,
    id_A int8 not null,
    id_B int8 not null,
    id_C int8 null,
    constraint pk_my_table primary key (id),
    constraint u_constrainte unique (id_A, id_B, id_C)
);

وأنا أريد (id_A, id_B, id_C) أن تكون متميزة في أي موقف. لذلك يجب أن يؤدي الإدخالان التاليان إلى حدوث خطأ:

INSERT INTO my_table VALUES (1, 1, 2, NULL);
INSERT INTO my_table VALUES (2, 1, 2, NULL);

ولكن لا يتصرف كما هو متوقع لأنه وفقًا للتوثيق ، لا تتم مقارنة قيمتين NULL مع بعضهما البعض ، لذلك تمر كلا الإدخالات دون أخطاء.

كيف يمكنني ضمان القيد الفريد الخاص بي حتى لو id_C يمكن أن يكون NULL في هذه الحالة؟ في الواقع ، السؤال الحقيقي هو: هل يمكنني ضمان هذا النوع من التفرد في "sql خالص" أو هل يجب علي تنفيذه على مستوى أعلى (Java في حالتي)؟

102
Manuel Leduc

يمكنك القيام بذلك في SQL خالص . أنشئ فهرس فريد جزئي بالإضافة إلى الذي لديك:

CREATE UNIQUE INDEX ab_c_null_idx ON my_table (id_A, id_B) WHERE id_C IS NULL;

بهذه الطريقة يمكنك الدخول لـ (a, b, c) في جدولك:

(1, 2, 1)
(1, 2, 2)
(1, 2, NULL)

لكن لا شيء من هذه المرة الثانية.

أو استخدم مؤشرين جزئيين UNIQUE ولا يوجد فهرس كامل (أو قيد). يعتمد الحل الأفضل على تفاصيل متطلباتك. قارن:

على الرغم من أن هذا يعد عاملاً فعالًا لعمود واحد غير قابل للإلغاء في فهرس UNIQUE ، فإنه يخرج عن نطاق السيطرة بسرعة للحصول على المزيد. مناقشة هذا - وكيفية استخدام UPSERT مع فهارس جزئية:

جانبا

لا تستخدم معرفات الحالات المختلطة بدون علامات اقتباس مزدوجة في PostgreSQL.

أنت might ضع في الاعتبار [serial العمود كمفتاح أساسي أو IDENTITY العمود في Postgres 10 أو في وقت لاحق. ذات صلة:

وبالتالي:

CREATE TABLE my_table (
   my_table_id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY  -- for pg 10+
-- my_table_id bigserial PRIMARY KEY  -- for pg 9.6 or older
 , id_a int8 NOT NULL
 , id_b int8 NOT NULL
 , id_c int8
 , CONSTRAINT u_constraint UNIQUE (id_a, id_b, id_c)
);

إذا كنت لا تتوقع أكثر من 2 مليار صف (> 2147483647) على مدار عمر الجدول (بما في ذلك النفايات والصفوف المحذوفة) ، فكر في integer (4 بايت) بدلاً من bigint (8 بايت).

102
Erwin Brandstetter

لقد واجهت نفس المشكلة ووجدت طريقة أخرى للحصول على NULL فريد في الجدول.

CREATE UNIQUE INDEX index_name ON table_name( COALESCE( foreign_key_field, -1) )

في حالتي المجال foreign_key_field هو عدد صحيح موجب ولن يكون -1.

لذا ، للرد على Manual Leduc ، يمكن أن يكون هناك حل آخر

CREATE UNIQUE INDEX  u_constrainte (COALESCE(id_a, -1), COALESCE(id_b,-1),COALESCE(id_c, -1) )

أفترض أن المعرفات لن تكون -1.

ما هي ميزة إنشاء فهرس جزئي؟
في حالة عدم وجود جملة NOT NULL ، id_a ، id_b و id_c يمكن أن يكون NULL معًا مرة واحدة فقط.
باستخدام فهرس جزئي ، يمكن أن تكون الحقول الثلاثة فارغة (NULL) أكثر من مرة.

12
Luc M

يمكن أن يعني Null أن القيمة غير معروفة لهذا الصف في الوقت الحالي ولكن ستتم إضافتها ، عندما تكون معروفة ، في المستقبل (على سبيل المثال FinishDate لتشغيل Project) أو أنه لا يمكن تحديد قيمة تم تطبيقه لهذا الصف (مثال EscapeVelocity لثقب أسود Star).

في رأيي ، من الأفضل عادةً تطبيع الجداول عن طريق التخلص من جميع القيم الخالية.

في حالتك ، تريد السماح بـ NULLs في العمود الخاص بك ، ولكنك تريد السماح فقط بـ NULL. لماذا ا؟ ما نوع هذه العلاقة بين الجدولين؟

ربما يمكنك ببساطة تغيير العمود إلى NOT NULL وتخزين ، بدلاً من NULL ، قيمة خاصة (مثل -1) المعروف أنه لن يظهر أبدًا. سيؤدي ذلك إلى حل مشكلة قيود التفرد (ولكن قد يكون لها آثار جانبية أخرى غير مرغوب فيها. على سبيل المثال ، استخدام -1 يعني "غير معروف/لا ينطبق" سوف يحرف أي مبلغ أو متوسط ​​الحسابات على العمود. أو كل هذه الحسابات يجب أن تأخذ في الاعتبار القيمة الخاصة وتتجاهلها.)

8
ypercubeᵀᴹ