#sql #postgresql #null #range #constraints
#sql #postgresql #null #диапазон #ограничения
Вопрос:
У меня есть таблица со столбцом, который содержит дату, действительную до, и я хочу убедиться, что для нее можно установить значение null только в одной строке в таблице. Есть ли простой способ сделать это?
Моя таблица выглядит следующим образом (postgres):
CREATE TABLE 123.myTable(
some_id integer NOT NULL,
valid_from timestamp without time zone NOT NULL DEFAULT now(),
valid_until timestamp without time zone,
someString character varying)
some_id
и valid_from
это мой PK. Я хочу, чтобы никто не вводил строку с нулевым значением в столбец valid_until, если для этого PK уже есть строка с нулевым значением.
Спасибо
Комментарии:
1. Что вы имеете в виду «для этого может быть установлено значение null только в одной строке для одного первичного ключа»? PK по определению применяется только к одной строке. PK не может содержать
NULL
значения в ключевых столбцах. Также какую СУБД вы используете?2. Нам действительно нужно лучшее объяснение здесь, чтобы помочь вам. Можете ли вы также предоставить нам макет таблицы и пример?
3. «some_id и valid_from — это мой PK». Откуда вы знаете? В опубликованном вами заявлении CREATE TABLE нет первичного ключа.
Ответ №1:
В PostgreSQL у вас есть два основных подхода.
- Используйте
'infinity'
вместо null. Тогда ваше уникальное ограничение работает так, как ожидалось. Или если вы не можете этого сделать: CREATE UNIQUE INDEX null_valid_from ON mytable(someid) where valid_until IS NULL
Я использовал оба подхода. Обычно я нахожу, что первый подход более чистый, и он позволяет вам лучше использовать типы диапазонов и исключать ограничения в более новых версиях PostgreSQL (чтобы гарантировать, что никакие два временных диапазона не перекрываются на основе заданного someid), но второй подход часто полезен там, где первый не может быть выполнен.
Ответ №2:
В зависимости от базы данных, вы не можете иметь null в первичном ключе (я не знаю обо всех базах данных, но в sql server вы не можете). Самый простой способ обойти это, который я могу придумать, — установить для даты минимальное значение, а затем добавить к нему уникальное ограничение или установить его в качестве первичного ключа.
Я полагаю, что другим способом было бы настроить триггер для проверки других значений в таблице, чтобы увидеть, является ли другая запись null, и если она есть, не разрешайте вставку.
Комментарии:
1. Я полагаю, что OP хочет убедиться, что поле ValidUntil date имеет значение NULL только один раз, а не сам PK.
Ответ №3:
Как сказал Кевин в своем ответе, вы можете настроить триггер базы данных, чтобы запретить кому-либо вставлять более одной строки, где действительна дата до NULL
.
Оператор SQL, который проверяет это условие, является:
SELECT COUNT(*)
FROM TABLE
WHERE valid until IS NULL;
Если количество не равно 1, значит, в вашей таблице проблема.
Процесс, который добавляет строку в эту таблицу, должен выполнить следующее:
- Найдите строку, в которой допустимое значение until равно
NULL
- Обновите действительное значение until до текущей даты или какой-либо другой значимой даты
- Вставьте новую строку с допустимым значением, равным
NULL
Ответ №4:
Я предполагаю, что вы храните записи с действующей датой и также используете действительную дату начала.
Если это так, вы могли бы использовать хранимые процедуры CRUD для обеспечения этого соответствия. Например, вставка закрывает все допустимые даты с нулевым значением перед вставкой новой записи с допустимой датой с нулевым значением.
Вероятно, вам нужна проверка другой хранимой процедуры, чтобы избежать перекрытия записей и разрешить удаление и редактирование записей. Возможно, было бы эффективнее (с точки зрения предложений where / более быстрых запросов) использовать дату в далеком будущем, а не использовать null.
Комментарии:
1. Я был бы очень осторожен с подходом SP. Легко написать что-то, что выглядит так, как будто оно работает, но не выполняется при наличии параллельных транзакций.
2. вы можете довольно хорошо смягчить проблемы параллелизма — для большинства сценариев это никогда не является проблемой. Я довольно часто использую этот метод для таких вещей, как налоговые ставки и другие относительно статичные данные. требуется немного больше информации из OP, чтобы увидеть, правильное ли это решение.
Ответ №5:
Я достаточно подробно знаю только Oracle, но то же самое может работать в других базах данных:
-
создайте другой столбец, который всегда содержит фиксированное значение (скажем, ‘0’), включите этот столбец в свой уникальный ключ.
-
Используйте не NULL, а конкретное очень высокое или низкое значение. Во многих случаях это на самом деле проще использовать, чем нулевое значение
-
Создайте уникальный ключ на основе функции, преобразующий дату, включая значение null, в какое-либо другое значение (например, строковое представление для дат и ‘x’ для null)
-
создайте материализованное представление, которое обновляется при каждом изменении в вашей основной таблице, и наложите ограничение на это представление.
выберите count (*) cnt из таблицы, где valid_until равно нулю
может работать как оператор select. И контрольное ограничение, ограничивающее значение cnt значениями 0 и 1
Ответ №6:
Я бы предложил вставить в эту таблицу через SP и поместить туда ваше ограничение, поскольку триггеры довольно скрыты и, скорее всего, о них забудут. Если это не вариант, сработает следующий триггер:
CREATE TABLE dbo.TESTTRIGGER
(
YourDate Date NULL
)
CREATE TRIGGER DupNullDates
ON dbo.TESTTRIGGER
FOR INSERT, UPDATE
AS
DECLARE @nullCount int
SELECT @nullCount = (SELECT COUNT(*) FROM TESTTRIGGER WHERE YourDate IS NULL)
IF(@NullCount > 1)
BEGIN
RAISERROR('Cannot have Multiple Nulls', 16, 1)
ROLLBACK TRAN
END
GO
Ответ №7:
Ну, если вы используете MS SQL, вы можете просто добавить уникальный индекс в этот столбец. Это допустит только одно значение NULL. Я предполагаю, что если вы используете другую СУБД, это все равно будет функционировать.
Комментарии:
1. Обратите внимание, что такое поведение противоречит стандарту SQL для
UNIQUE
ограничений, поэтому я был бы склонен думать, что оно, как правило, не функционирует в других продуктах SQL (или СУБД без ‘R’ — пожалуйста, не предполагайте, что SQL является реляционным 🙂