Составной первичный ключ применяет ограничения NOT NULL для вовлеченных столбцов

это одно странное, нежелательное поведение, с которым я столкнулся в Postgres: Когда я создаю таблицу Postgres с составными первичными ключами, она применяет ограничение NOT NULL для каждого столбца составной комбинации.

например,

CREATE TABLE distributors (m_id integer, x_id integer, PRIMARY KEY(m_id, x_id));

обеспечивает NOT NULL ограничения на столбцы m_id и x_id, которого я не хочу! MySQL этого не делает. Я думаю, что Oracle этого не делает.

Я понимаю, что PRIMARY KEY обеспечивает UNIQUE и NOT NULL автоматически, но это имеет смысл для одного столбца первичного ключа. В таблице первичного ключа с несколькими столбцами уникальность определяется комбинацией.

есть ли простой способ избежать такого поведения Postgres?
Если я выполню это:

CREATE TABLE distributors (m_id integer, x_id integer);

Я не получаю никаких NOT NULL ограничениями, конечно.

1 ответов


если вы нужно чтобы разрешить значения NULL, используйте UNIQUE ограничения вместо PRIMARY KEY (и добавьте суррогатный столбец PK, я предлагаю serial). Это позволяет столбцам быть NULL:

CREATE TABLE distributor (
   distributor_id serial PRIMARY KEY
 , m_id integer
 , x_id integer
 , UNIQUE(m_id, x_id)
);

Примечание, однако (в документации):

для уникального ограничения значения null не считаются равными.

в вашей case, вы можете войти (1, NULL) на (m_id, x_id) любое количество раз без нарушения ограничений. Postgres никогда не учитывает два значения NULL равной - согласно определению в стандарте SQL.

Если вам нужно лечить NULL значения, равные для запрета таких "дубликатов",я вижу два варианта:

1. Два частичных индекса

кроме того до UNIQUE ограничение выше:

CREATE UNIQUE INDEX dist_m_uni_idx ON distributor (m_id) WHERE x_id IS NULL;
CREATE UNIQUE INDEX dist_x_uni_idx ON distributor (x_id) WHERE m_id IS NULL;

но это быстро выходит из-под контроля с более чем двумя столбцами, которые могут быть NULL.
По теме:

2. Многоколоночный уникальный индекс для выражений

вместо уникальности. Нам нужно свободное значение по умолчанию, которое никогда не присутствует в задействованных столбцах, например -1. Добавить CHECK ограничения, чтобы запретить его:

CREATE TABLE distributor (
   distributor serial PRIMARY KEY
 , m_id integer
 , x_id integer
 , CHECK (m_id &lt> -1)
 , CHECK (x_id &lt> -1)
);
CREATE UNIQUE INDEX distributor_uni_idx ON distributor (COALESCE(m_id, -1)
                                                      , COALESCE(x_id, -1))

как некоторые СУБД обрабатывают вещи не всегда является полезным индикатором правильного поведения. The руководство Postgres намекает на это:

это означает, что даже при наличии уникального ограничения можно для хранения повторяющихся строк, содержащих значение null, по крайней мере в одной из в Столбцах. Это поведение соответствует стандарту SQL, но!--17-->мы слышали, что другие базы данных SQL могут не следовать этому правилу. Так что будьте осторожны, когда разработка приложений, которые должны быть портативными.

жирным выделено мной.